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