xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/nssov/nssov.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
1 /* nssov.c - nss-ldap overlay for slapd */
2 /* $OpenLDAP: pkg/ldap/contrib/slapd-modules/nssov/nssov.c,v 1.1.2.1 2008/07/08 18:53:57 quanah Exp $ */
3 /*
4  * Copyright 2008 by Howard Chu, Symas Corp.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 /*
16  * This code references portions of the nss-ldapd package
17  * written by Arthur de Jong. The nss-ldapd code was forked
18  * from the nss-ldap library written by Luke Howard.
19  */
20 
21 #include "nssov.h"
22 
23 #ifndef SLAPD_OVER_NSSOV
24 #define SLAPD_OVER_NSSOV SLAPD_MOD_DYNAMIC
25 #endif
26 
27 #include "../slapd/config.h"	/* not nss-ldapd config.h */
28 
29 #include "lutil.h"
30 
31 #include <ac/errno.h>
32 #include <ac/unistd.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 
36 /* buffer sizes for I/O */
37 #define READBUFFER_MINSIZE 32
38 #define READBUFFER_MAXSIZE 64
39 #define WRITEBUFFER_MINSIZE 64
40 #define WRITEBUFFER_MAXSIZE 64*1024
41 
42 /* Find the given attribute's value in the RDN of the DN */
43 int nssov_find_rdnval(struct berval *dn, AttributeDescription *ad, struct berval *value)
44 {
45 	struct berval rdn;
46 	char *next;
47 
48 	BER_BVZERO(value);
49 	dnRdn( dn, &rdn );
50 	do {
51 		next = ber_bvchr( &rdn, '+' );
52 		if ( rdn.bv_val[ad->ad_cname.bv_len] == '=' &&
53 			!ber_bvcmp( &rdn, &ad->ad_cname )) {
54 			if ( next )
55 				rdn.bv_len = next - rdn.bv_val;
56 			value->bv_val = rdn.bv_val + ad->ad_cname.bv_len + 1;
57 			value->bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
58 			break;
59 		}
60 		if ( !next )
61 			break;
62 		next++;
63 		rdn.bv_len -= next - rdn.bv_val;
64 		rdn.bv_val = next;
65 	} while (1);
66 }
67 
68 /* create a search filter using a name that requires escaping */
69 int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf)
70 {
71 	char buf2[1024];
72 	struct berval bv2 = {sizeof(buf2),buf2};
73 
74 	/* escape attribute */
75 	if (nssov_escape(name,&bv2))
76 		return -1;
77 	/* build filter */
78 	if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
79 		buf->bv_len )
80 		return -1;
81 	buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
82 		mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
83 		bv2.bv_val );
84 	return 0;
85 }
86 
87 /* create a search filter using a string converted from an int */
88 int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf)
89 {
90 	/* build filter */
91 	if (id->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
92 		buf->bv_len )
93 		return -1;
94 	buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
95 		mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
96 		id->bv_val );
97 	return 0;
98 }
99 
100 void get_userpassword(struct berval *attr,struct berval *pw)
101 {
102 	int i;
103 	/* go over the entries and return the remainder of the value if it
104 		 starts with {crypt} or crypt$ */
105 	for (i=0;!BER_BVISNULL(&attr[i]);i++)
106 	{
107 		if (strncasecmp(attr[i].bv_val,"{crypt}",7)==0) {
108 			pw->bv_val = attr[i].bv_val + 7;
109 			pw->bv_len = attr[i].bv_len - 7;
110 			return;
111 		}
112 		if (strncasecmp(attr[i].bv_val,"crypt$",6)==0) {
113 			pw->bv_val = attr[i].bv_val + 6;
114 			pw->bv_len = attr[i].bv_len - 6;
115 			return;
116 		}
117 	}
118 	/* just return the first value completely */
119 	*pw = *attr;
120 	/* TODO: support more password formats e.g. SMD5
121 		(which is $1$ but in a different format)
122 		(any code for this is more than welcome) */
123 }
124 
125 /* this writes a single address to the stream */
126 int write_address(TFILE *fp,struct berval *addr)
127 {
128 	int32_t tmpint32;
129 	struct in_addr ipv4addr;
130 	struct in6_addr ipv6addr;
131 	/* try to parse the address as IPv4 first, fall back to IPv6 */
132 	if (inet_pton(AF_INET,addr->bv_val,&ipv4addr)>0)
133 	{
134 		/* write address type */
135 		WRITE_INT32(fp,AF_INET);
136 		/* write the address length */
137 		WRITE_INT32(fp,sizeof(struct in_addr));
138 		/* write the address itself (in network byte order) */
139 		WRITE_TYPE(fp,ipv4addr,struct in_addr);
140 	}
141 	else if (inet_pton(AF_INET6,addr->bv_val,&ipv6addr)>0)
142 	{
143 		/* write address type */
144 		WRITE_INT32(fp,AF_INET6);
145 		/* write the address length */
146 		WRITE_INT32(fp,sizeof(struct in6_addr));
147 		/* write the address itself (in network byte order) */
148 		WRITE_TYPE(fp,ipv6addr,struct in6_addr);
149 	}
150 	else
151 	{
152 		/* failure, log but write simple invalid address
153 			 (otherwise the address list is messed up) */
154 		/* TODO: have error message in correct format */
155 		Debug(LDAP_DEBUG_ANY,"nssov: unparseable address: %s",addr->bv_val,0,0);
156 		/* write an illegal address type */
157 		WRITE_INT32(fp,-1);
158 		/* write an empty address */
159 		WRITE_INT32(fp,0);
160 	}
161 	/* we're done */
162 	return 0;
163 }
164 
165 int read_address(TFILE *fp,char *addr,int *addrlen,int *af)
166 {
167 	int32_t tmpint32;
168 	int len;
169 	/* read address family */
170 	READ_INT32(fp,*af);
171 	if ((*af!=AF_INET)&&(*af!=AF_INET6))
172 	{
173 		Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d",*af,0,0);
174 		return -1;
175 	}
176 	/* read address length */
177 	READ_INT32(fp,len);
178 	if ((len>*addrlen)||(len<=0))
179 	{
180 		Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d",len,0,0);
181 		return -1;
182 	}
183 	*addrlen=len;
184 	/* read address */
185 	READ(fp,addr,len);
186 	/* we're done */
187 	return 0;
188 }
189 
190 int nssov_escape(struct berval *src,struct berval *dst)
191 {
192 	size_t pos=0;
193 	int i;
194 	/* go over all characters in source string */
195 	for (i=0;i<src->bv_len;i++)
196 	{
197 		/* check if char will fit */
198 		if (pos>=(dst->bv_len-4))
199 			return -1;
200 		/* do escaping for some characters */
201 		switch (src->bv_val[i])
202 		{
203 			case '*':
204 				strcpy(dst->bv_val+pos,"\\2a");
205 				pos+=3;
206 				break;
207 			case '(':
208 				strcpy(dst->bv_val+pos,"\\28");
209 				pos+=3;
210 				break;
211 			case ')':
212 				strcpy(dst->bv_val+pos,"\\29");
213 				pos+=3;
214 				break;
215 			case '\\':
216 				strcpy(dst->bv_val+pos,"\\5c");
217 				pos+=3;
218 				break;
219 			default:
220 				/* just copy character */
221 				dst->bv_val[pos++]=src->bv_val[i];
222 				break;
223 		}
224 	}
225 	/* terminate destination string */
226 	dst->bv_val[pos]='\0';
227 	dst->bv_len = pos;
228 	return 0;
229 }
230 
231 /* read the version information and action from the stream
232    this function returns the read action in location pointer to by action */
233 static int read_header(TFILE *fp,int32_t *action)
234 {
235   int32_t tmpint32;
236   /* read the protocol version */
237   READ_TYPE(fp,tmpint32,int32_t);
238   if (tmpint32 != (int32_t)NSLCD_VERSION)
239   {
240     Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)",(int)tmpint32,0,0);
241     return -1;
242   }
243   /* read the request type */
244   READ(fp,action,sizeof(int32_t));
245   return 0;
246 }
247 
248 /* read a request message, returns <0 in case of errors,
249    this function closes the socket */
250 static void handleconnection(nssov_info *ni,int sock,Operation *op)
251 {
252   TFILE *fp;
253   int32_t action;
254   struct timeval readtimeout,writetimeout;
255   uid_t uid;
256   gid_t gid;
257   char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")];
258 
259   /* log connection */
260   if (lutil_getpeereid(sock,&uid,&gid))
261     Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s",strerror(errno),0,0);
262   else
263     Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d",
264                       (int)uid,(int)gid,0);
265 
266   /* Should do authid mapping too */
267   op->o_dn.bv_len = sprintf(authid,"gidNumber=%d+uidNumber=%d,cn=peercred,cn=external,cn=auth",
268   	(int)uid, (int)gid );
269   op->o_dn.bv_val = authid;
270   op->o_ndn = op->o_dn;
271 
272   /* set the timeouts */
273   readtimeout.tv_sec=0; /* clients should send their request quickly */
274   readtimeout.tv_usec=500000;
275   writetimeout.tv_sec=5; /* clients could be taking some time to process the results */
276   writetimeout.tv_usec=0;
277   /* create a stream object */
278   if ((fp=tio_fdopen(sock,&readtimeout,&writetimeout,
279                      READBUFFER_MINSIZE,READBUFFER_MAXSIZE,
280                      WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL)
281   {
282     Debug( LDAP_DEBUG_ANY,"nssov: cannot create stream for writing: %s",strerror(errno),0,0);
283     (void)close(sock);
284     return;
285   }
286   /* read request */
287   if (read_header(fp,&action))
288   {
289     (void)tio_close(fp);
290     return;
291   }
292   /* handle request */
293   switch (action)
294   {
295     case NSLCD_ACTION_ALIAS_BYNAME:     (void)nssov_alias_byname(ni,fp,op); break;
296     case NSLCD_ACTION_ALIAS_ALL:        (void)nssov_alias_all(ni,fp,op); break;
297     case NSLCD_ACTION_ETHER_BYNAME:     (void)nssov_ether_byname(ni,fp,op); break;
298     case NSLCD_ACTION_ETHER_BYETHER:    (void)nssov_ether_byether(ni,fp,op); break;
299     case NSLCD_ACTION_ETHER_ALL:        (void)nssov_ether_all(ni,fp,op); break;
300     case NSLCD_ACTION_GROUP_BYNAME:     (void)nssov_group_byname(ni,fp,op); break;
301     case NSLCD_ACTION_GROUP_BYGID:      (void)nssov_group_bygid(ni,fp,op); break;
302     case NSLCD_ACTION_GROUP_BYMEMBER:   (void)nssov_group_bymember(ni,fp,op); break;
303     case NSLCD_ACTION_GROUP_ALL:        (void)nssov_group_all(ni,fp,op); break;
304     case NSLCD_ACTION_HOST_BYNAME:      (void)nssov_host_byname(ni,fp,op); break;
305     case NSLCD_ACTION_HOST_BYADDR:      (void)nssov_host_byaddr(ni,fp,op); break;
306     case NSLCD_ACTION_HOST_ALL:         (void)nssov_host_all(ni,fp,op); break;
307     case NSLCD_ACTION_NETGROUP_BYNAME:  (void)nssov_netgroup_byname(ni,fp,op); break;
308     case NSLCD_ACTION_NETWORK_BYNAME:   (void)nssov_network_byname(ni,fp,op); break;
309     case NSLCD_ACTION_NETWORK_BYADDR:   (void)nssov_network_byaddr(ni,fp,op); break;
310     case NSLCD_ACTION_NETWORK_ALL:      (void)nssov_network_all(ni,fp,op); break;
311     case NSLCD_ACTION_PASSWD_BYNAME:    (void)nssov_passwd_byname(ni,fp,op); break;
312     case NSLCD_ACTION_PASSWD_BYUID:     (void)nssov_passwd_byuid(ni,fp,op); break;
313     case NSLCD_ACTION_PASSWD_ALL:       (void)nssov_passwd_all(ni,fp,op); break;
314     case NSLCD_ACTION_PROTOCOL_BYNAME:  (void)nssov_protocol_byname(ni,fp,op); break;
315     case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nssov_protocol_bynumber(ni,fp,op); break;
316     case NSLCD_ACTION_PROTOCOL_ALL:     (void)nssov_protocol_all(ni,fp,op); break;
317     case NSLCD_ACTION_RPC_BYNAME:       (void)nssov_rpc_byname(ni,fp,op); break;
318     case NSLCD_ACTION_RPC_BYNUMBER:     (void)nssov_rpc_bynumber(ni,fp,op); break;
319     case NSLCD_ACTION_RPC_ALL:          (void)nssov_rpc_all(ni,fp,op); break;
320     case NSLCD_ACTION_SERVICE_BYNAME:   (void)nssov_service_byname(ni,fp,op); break;
321     case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nssov_service_bynumber(ni,fp,op); break;
322     case NSLCD_ACTION_SERVICE_ALL:      (void)nssov_service_all(ni,fp,op); break;
323     case NSLCD_ACTION_SHADOW_BYNAME:    if (uid==0) (void)nssov_shadow_byname(ni,fp,op); break;
324     case NSLCD_ACTION_SHADOW_ALL:       if (uid==0) (void)nssov_shadow_all(ni,fp,op); break;
325     default:
326       Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action,0,0);
327       break;
328   }
329   /* we're done with the request */
330   (void)tio_close(fp);
331   return;
332 }
333 
334 /* accept a connection on the socket */
335 static void *acceptconn(void *ctx, void *arg)
336 {
337 	nssov_info *ni = arg;
338 	Connection conn = {0};
339 	OperationBuffer opbuf;
340 	Operation *op;
341 	int csock;
342 
343 	if ( slapd_shutdown )
344 		return NULL;
345 
346 	{
347 		struct sockaddr_storage addr;
348 		socklen_t alen;
349 		int j;
350 
351 		/* accept a new connection */
352 		alen=(socklen_t)sizeof(struct sockaddr_storage);
353 		csock=accept(ni->ni_socket,(struct sockaddr *)&addr,&alen);
354 		connection_client_enable(ni->ni_conn);
355 		if (csock<0)
356 		{
357 			if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK))
358 			{
359 				Debug( LDAP_DEBUG_TRACE,"nssov: accept() failed (ignored): %s",strerror(errno),0,0);
360 				return;
361 			}
362 			Debug( LDAP_DEBUG_ANY,"nssov: accept() failed: %s",strerror(errno),0,0);
363 			return;
364 		}
365 		/* make sure O_NONBLOCK is not inherited */
366 		if ((j=fcntl(csock,F_GETFL,0))<0)
367 		{
368 			Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_GETFL) failed: %s",strerror(errno),0,0);
369 			if (close(csock))
370 				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
371 			return;
372 		}
373 		if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0)
374 		{
375 			Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,~O_NONBLOCK) failed: %s",strerror(errno),0,0);
376 			if (close(csock))
377 				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
378 			return;
379 		}
380 	}
381 	connection_fake_init( &conn, &opbuf, ctx );
382 	op=&opbuf.ob_op;
383 	op->o_bd = ni->ni_db;
384 	op->o_tag = LDAP_REQ_SEARCH;
385 
386 	/* handle the connection */
387 	handleconnection(ni,csock,op);
388 }
389 
390 static slap_verbmasks nss_svcs[] = {
391 	{ BER_BVC("alias"), NM_alias },
392 	{ BER_BVC("ether"), NM_ether },
393 	{ BER_BVC("group"), NM_group },
394 	{ BER_BVC("host"), NM_host },
395 	{ BER_BVC("netgroup"), NM_netgroup },
396 	{ BER_BVC("network"), NM_network },
397 	{ BER_BVC("passwd"), NM_passwd },
398 	{ BER_BVC("protocol"), NM_protocol },
399 	{ BER_BVC("rpc"), NM_rpc },
400 	{ BER_BVC("service"), NM_service },
401 	{ BER_BVC("shadow"), NM_shadow },
402 	{ BER_BVNULL, 0 }
403 };
404 
405 enum {
406 	NSS_SSD=1,
407 	NSS_MAP
408 };
409 
410 static ConfigDriver nss_cf_gen;
411 
412 static ConfigTable nsscfg[] = {
413 	{ "nssov-ssd", "service> <url", 3, 3, 0, ARG_MAGIC|NSS_SSD,
414 		nss_cf_gen, "(OLcfgCtAt:3.1 NAME 'olcNssSsd' "
415 			"DESC 'URL for searches in a given service' "
416 			"EQUALITY caseIgnoreMatch "
417 			"SYNTAX OMsDirectoryString )", NULL, NULL },
418 	{ "nssov-map", "service> <orig> <new", 4, 4, 0, ARG_MAGIC|NSS_MAP,
419 		nss_cf_gen, "(OLcfgCtAt:3.2 NAME 'olcNssMap' "
420 			"DESC 'Map <service> lookups of <orig> attr to <new> attr' "
421 			"EQUALITY caseIgnoreMatch "
422 			"SYNTAX OMsDirectoryString )", NULL, NULL },
423 	{ NULL, NULL, 0,0,0, ARG_IGNORED }
424 };
425 
426 static ConfigOCs nssocs[] = {
427 	{ "( OLcfgCtOc:3.1 "
428 		"NAME 'olcNssOvConfig' "
429 		"DESC 'NSS lookup configuration' "
430 		"SUP olcOverlayConfig "
431 		"MAY ( olcNssSsd $ olcNssMap ) )",
432 		Cft_Overlay, nsscfg },
433 	{ NULL, 0, NULL }
434 };
435 
436 static int
437 nss_cf_gen(ConfigArgs *c)
438 {
439 	slap_overinst *on = (slap_overinst *)c->bi;
440 	nssov_info *ni = on->on_bi.bi_private;
441 	nssov_mapinfo *mi;
442 	int i, j, rc = 0;
443 
444 	if ( c->op == SLAP_CONFIG_EMIT ) {
445 		switch(c->type) {
446 		case NSS_SSD:
447 			rc = 1;
448 			for (i=NM_alias;i<NM_NONE;i++) {
449 				struct berval scope;
450 				struct berval ssd;
451 				struct berval base;
452 
453 				mi = &ni->ni_maps[i];
454 
455 				/* ignore all-default services */
456 				if ( mi->mi_scope == LDAP_SCOPE_DEFAULT &&
457 					bvmatch( &mi->mi_filter, &mi->mi_filter0 ) &&
458 					BER_BVISNULL( &mi->mi_base ))
459 					continue;
460 
461 				if ( BER_BVISNULL( &mi->mi_base ))
462 					base = ni->ni_db->be_nsuffix[0];
463 				else
464 					base = mi->mi_base;
465 				ldap_pvt_scope2bv(mi->mi_scope == LDAP_SCOPE_DEFAULT ?
466 					LDAP_SCOPE_SUBTREE : mi->mi_scope, &scope);
467 				ssd.bv_len = STRLENOF(" ldap:///???") + nss_svcs[i].word.bv_len +
468 					base.bv_len + scope.bv_len + mi->mi_filter.bv_len;
469 				ssd.bv_val = ch_malloc( ssd.bv_len + 1 );
470 				sprintf(ssd.bv_val, "%s ldap:///%s??%s?%s", nss_svcs[i].word.bv_val,
471 					base.bv_val, scope.bv_val, mi->mi_filter.bv_val );
472 				ber_bvarray_add( &c->rvalue_vals, &ssd );
473 				rc = 0;
474 			}
475 			break;
476 		case NSS_MAP:
477 			rc = 1;
478 			for (i=NM_alias;i<NM_NONE;i++) {
479 				int j;
480 
481 				mi = &ni->ni_maps[i];
482 				for (j=0;!BER_BVISNULL(&mi->mi_attrkeys[j]);j++) {
483 					if ( ber_bvstrcasecmp(&mi->mi_attrkeys[j],
484 						&mi->mi_attrs[j].an_name)) {
485 						struct berval map;
486 
487 						map.bv_len = nss_svcs[i].word.bv_len +
488 							mi->mi_attrkeys[j].bv_len +
489 							mi->mi_attrs->an_desc->ad_cname.bv_len + 2;
490 						map.bv_val = ch_malloc(map.bv_len + 1);
491 						sprintf(map.bv_val, "%s %s %s", nss_svcs[i].word.bv_val,
492 							mi->mi_attrkeys[j].bv_val, mi->mi_attrs->an_desc->ad_cname.bv_val );
493 						ber_bvarray_add( &c->rvalue_vals, &map );
494 						rc = 0;
495 					}
496 				}
497 			}
498 			break;
499 		}
500 		return rc;
501 	} else if ( c->op == LDAP_MOD_DELETE ) {
502 		return 1;
503 	}
504 	switch( c->type ) {
505 	case NSS_SSD: {
506 		LDAPURLDesc *lud;
507 
508 		i = verb_to_mask(c->argv[1], nss_svcs);
509 		if ( i == NM_NONE )
510 			return 1;
511 
512 		mi = &ni->ni_maps[i];
513 		rc = ldap_url_parse(c->argv[2], &lud);
514 		if ( rc )
515 			return 1;
516 		do {
517 			struct berval base;
518 			/* Must be LDAP scheme */
519 			if (strcasecmp(lud->lud_scheme,"ldap")) {
520 				rc = 1;
521 				break;
522 			}
523 			/* Host part, attrs, and extensions must be empty */
524 			if (( lud->lud_host && *lud->lud_host ) ||
525 				lud->lud_attrs || lud->lud_exts ) {
526 				rc = 1;
527 				break;
528 			}
529 			ber_str2bv( lud->lud_dn,0,0,&base);
530 			rc = dnNormalize( 0,NULL,NULL,&base,&mi->mi_base,NULL);
531 			if ( rc )
532 				break;
533 			if ( lud->lud_filter ) {
534 				/* steal this */
535 				ber_str2bv( lud->lud_filter,0,0,&mi->mi_filter);
536 				lud->lud_filter = NULL;
537 			}
538 			mi->mi_scope = lud->lud_scope;
539 		} while(0);
540 		ldap_free_urldesc( lud );
541 		}
542 		break;
543 	case NSS_MAP:
544 		i = verb_to_mask(c->argv[1], nss_svcs);
545 		if ( i == NM_NONE )
546 			return 1;
547 		rc = 1;
548 		mi = &ni->ni_maps[i];
549 		for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
550 			if (!strcasecmp(c->argv[2],mi->mi_attrkeys[j].bv_val)) {
551 				AttributeDescription *ad = NULL;
552 				const char *text;
553 				rc = slap_str2ad( c->argv[3], &ad, &text);
554 				if ( rc == 0 ) {
555 					mi->mi_attrs[j].an_desc = ad;
556 					mi->mi_attrs[j].an_name = ad->ad_cname;
557 				}
558 				break;
559 			}
560 		}
561 		break;
562 	}
563 	return rc;
564 }
565 
566 static int
567 nssov_db_init(
568 	BackendDB *be,
569 	ConfigReply *cr )
570 {
571 	slap_overinst *on = (slap_overinst *)be->bd_info;
572 	nssov_info *ni;
573 	nssov_mapinfo *mi;
574 	int i, j;
575 
576 	ni = ch_malloc( sizeof(nssov_info) );
577 	on->on_bi.bi_private = ni;
578 
579 	/* set up map keys */
580 	nssov_alias_init(ni);
581 	nssov_ether_init(ni);
582 	nssov_group_init(ni);
583 	nssov_host_init(ni);
584 	nssov_netgroup_init(ni);
585 	nssov_network_init(ni);
586 	nssov_passwd_init(ni);
587 	nssov_protocol_init(ni);
588 	nssov_rpc_init(ni);
589 	nssov_service_init(ni);
590 	nssov_shadow_init(ni);
591 
592 	ni->ni_db = be->bd_self;
593 
594 	return 0;
595 }
596 
597 static int
598 nssov_db_destroy(
599 	BackendDB *be,
600 	ConfigReply *cr )
601 {
602 }
603 
604 static int
605 nssov_db_open(
606 	BackendDB *be,
607 	ConfigReply *cr )
608 {
609 	slap_overinst *on = (slap_overinst *)be->bd_info;
610 	nssov_info *ni = on->on_bi.bi_private;
611 	nssov_mapinfo *mi;
612 
613 	int i, sock;
614 	struct sockaddr_un addr;
615 
616 	/* Set default bases */
617 	for (i=0; i<NM_NONE; i++) {
618 		if ( BER_BVISNULL( &ni->ni_maps[i].mi_base )) {
619 			ber_dupbv( &ni->ni_maps[i].mi_base, &be->be_nsuffix[0] );
620 		}
621 		if ( ni->ni_maps[i].mi_scope == LDAP_SCOPE_DEFAULT )
622 			ni->ni_maps[i].mi_scope = LDAP_SCOPE_SUBTREE;
623 	}
624 	/* validate attribute maps */
625 	mi = ni->ni_maps;
626 	for ( i=0; i<NM_NONE; i++,mi++) {
627 		const char *text;
628 		int j;
629 		for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
630 			/* skip attrs we already validated */
631 			if ( mi->mi_attrs[j].an_desc ) continue;
632 			if ( slap_bv2ad( &mi->mi_attrs[j].an_name,
633 				&mi->mi_attrs[j].an_desc, &text )) {
634 				Debug(LDAP_DEBUG_ANY,"nssov: invalid attr \"%s\": %s\n",
635 					mi->mi_attrs[j].an_name.bv_val, text, 0 );
636 				return -1;
637 			}
638 		}
639 		BER_BVZERO(&mi->mi_attrs[j].an_name);
640 		mi->mi_attrs[j].an_desc = NULL;
641 	}
642 
643 	if ( slapMode & SLAP_SERVER_MODE ) {
644 		/* create a socket */
645 		if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 )
646 		{
647 			Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s",strerror(errno),0,0);
648 			return -1;
649 		}
650 		/* remove existing named socket */
651 		if (unlink(NSLCD_SOCKET)<0)
652 		{
653 			Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s",
654 							strerror(errno),0,0);
655 		}
656 		/* create socket address structure */
657 		memset(&addr,0,sizeof(struct sockaddr_un));
658 		addr.sun_family=AF_UNIX;
659 		strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path));
660 		addr.sun_path[sizeof(addr.sun_path)-1]='\0';
661 		/* bind to the named socket */
662 		if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un)))
663 		{
664 			Debug( LDAP_DEBUG_ANY,"nssov: bind() to "NSLCD_SOCKET" failed: %s",
665 							strerror(errno),0,0);
666 			if (close(sock))
667 				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
668 			return -1;
669 		}
670 		/* close the file descriptor on exit */
671 		if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0)
672 		{
673 			Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,O_NONBLOCK) failed: %s",strerror(errno),0,0);
674 			if (close(sock))
675 				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
676 			return -1;
677 		}
678 		/* set permissions of socket so anybody can do requests */
679 		/* Note: we use chmod() here instead of fchmod() because
680 			 fchmod does not work on sockets
681 			 http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html
682 			 http://lkml.org/lkml/2005/5/16/11 */
683 		if (chmod(NSLCD_SOCKET,(mode_t)0666))
684 		{
685 			Debug( LDAP_DEBUG_ANY,"nssov: chmod(0666) failed: %s",strerror(errno),0,0);
686 			if (close(sock))
687 				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
688 			return -1;
689 		}
690 		/* start listening for connections */
691 		if (listen(sock,SOMAXCONN)<0)
692 		{
693 			Debug( LDAP_DEBUG_ANY,"nssov: listen() failed: %s",strerror(errno),0,0);
694 			if (close(sock))
695 				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",strerror(errno),0,0);
696 			return -1;
697 		}
698 		ni->ni_socket = sock;
699 		ni->ni_conn = connection_client_setup( sock, acceptconn, ni );
700 	}
701 
702 	return 0;
703 }
704 
705 static int
706 nssov_db_close(
707 	BackendDB *be,
708 	ConfigReply *cr )
709 {
710 	slap_overinst *on = (slap_overinst *)be->bd_info;
711 	nssov_info *ni = on->on_bi.bi_private;
712 
713 	/* close socket if it's still in use */
714 	if (ni->ni_socket >= 0);
715 	{
716 		if (close(ni->ni_socket))
717 			Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",strerror(errno),0,0);
718 		ni->ni_socket = -1;
719 	}
720 	/* remove existing named socket */
721 	if (unlink(NSLCD_SOCKET)<0)
722 	{
723 		Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s",
724 			strerror(errno),0,0);
725 	}
726 }
727 
728 static slap_overinst nssov;
729 
730 int
731 nssov_initialize( void )
732 {
733 	int rc;
734 
735 	nssov.on_bi.bi_type = "nssov";
736 	nssov.on_bi.bi_db_init = nssov_db_init;
737 	nssov.on_bi.bi_db_destroy = nssov_db_destroy;
738 	nssov.on_bi.bi_db_open = nssov_db_open;
739 	nssov.on_bi.bi_db_close = nssov_db_close;
740 
741 	nssov.on_bi.bi_cf_ocs = nssocs;
742 
743 	rc = config_register_schema( nsscfg, nssocs );
744 	if ( rc ) return rc;
745 
746 	return overlay_register(&nssov);
747 }
748 
749 #if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC
750 int
751 init_module( int argc, char *argv[] )
752 {
753 	return nssov_initialize();
754 }
755 #endif
756