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