1/* nssov.c - nss-ldap overlay for slapd */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2008-2011 The OpenLDAP Foundation.
6 * Portions Copyright 2008 by Howard Chu, Symas Corp.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* ACKNOWLEDGEMENTS:
18 * This code references portions of the nss-ldapd package
19 * written by Arthur de Jong. The nss-ldapd code was forked
20 * from the nss-ldap library written by Luke Howard.
21 */
22
23#include "nssov.h"
24
25#ifndef SLAPD_OVER_NSSOV
26#define SLAPD_OVER_NSSOV SLAPD_MOD_DYNAMIC
27#endif
28
29#include "../slapd/config.h"	/* not nss-ldapd config.h */
30
31#include "lutil.h"
32
33#include <ac/errno.h>
34#include <ac/unistd.h>
35#include <fcntl.h>
36#include <sys/stat.h>
37
38AttributeDescription *nssov_pam_host_ad;
39AttributeDescription *nssov_pam_svc_ad;
40
41/* buffer sizes for I/O */
42#define READBUFFER_MINSIZE 32
43#define READBUFFER_MAXSIZE 64
44#define WRITEBUFFER_MINSIZE 64
45#define WRITEBUFFER_MAXSIZE 64*1024
46
47/* Find the given attribute's value in the RDN of the DN */
48int nssov_find_rdnval(struct berval *dn, AttributeDescription *ad, struct berval *value)
49{
50	struct berval rdn;
51	char *next;
52
53	BER_BVZERO(value);
54	dnRdn( dn, &rdn );
55	do {
56		next = ber_bvchr( &rdn, '+' );
57		if ( rdn.bv_val[ad->ad_cname.bv_len] == '=' &&
58			!ber_bvcmp( &rdn, &ad->ad_cname )) {
59			if ( next )
60				rdn.bv_len = next - rdn.bv_val;
61			value->bv_val = rdn.bv_val + ad->ad_cname.bv_len + 1;
62			value->bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
63			break;
64		}
65		if ( !next )
66			break;
67		next++;
68		rdn.bv_len -= next - rdn.bv_val;
69		rdn.bv_val = next;
70	} while (1);
71}
72
73/* create a search filter using a name that requires escaping */
74int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf)
75{
76	char buf2[1024];
77	struct berval bv2 = {sizeof(buf2),buf2};
78
79	/* escape attribute */
80	if (nssov_escape(name,&bv2))
81		return -1;
82	/* build filter */
83	if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
84		buf->bv_len )
85		return -1;
86	buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
87		mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
88		bv2.bv_val );
89	return 0;
90}
91
92/* create a search filter using a string converted from an int */
93int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf)
94{
95	/* build filter */
96	if (id->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
97		buf->bv_len )
98		return -1;
99	buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
100		mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
101		id->bv_val );
102	return 0;
103}
104
105void get_userpassword(struct berval *attr,struct berval *pw)
106{
107	int i;
108	/* go over the entries and return the remainder of the value if it
109		 starts with {crypt} or crypt$ */
110	for (i=0;!BER_BVISNULL(&attr[i]);i++)
111	{
112		if (strncasecmp(attr[i].bv_val,"{crypt}",7)==0) {
113			pw->bv_val = attr[i].bv_val + 7;
114			pw->bv_len = attr[i].bv_len - 7;
115			return;
116		}
117		if (strncasecmp(attr[i].bv_val,"crypt$",6)==0) {
118			pw->bv_val = attr[i].bv_val + 6;
119			pw->bv_len = attr[i].bv_len - 6;
120			return;
121		}
122	}
123	/* just return the first value completely */
124	*pw = *attr;
125	/* TODO: support more password formats e.g. SMD5
126		(which is $1$ but in a different format)
127		(any code for this is more than welcome) */
128}
129
130/* this writes a single address to the stream */
131int write_address(TFILE *fp,struct berval *addr)
132{
133	int32_t tmpint32;
134	struct in_addr ipv4addr;
135	struct in6_addr ipv6addr;
136	/* try to parse the address as IPv4 first, fall back to IPv6 */
137	if (inet_pton(AF_INET,addr->bv_val,&ipv4addr)>0)
138	{
139		/* write address type */
140		WRITE_INT32(fp,AF_INET);
141		/* write the address length */
142		WRITE_INT32(fp,sizeof(struct in_addr));
143		/* write the address itself (in network byte order) */
144		WRITE_TYPE(fp,ipv4addr,struct in_addr);
145	}
146	else if (inet_pton(AF_INET6,addr->bv_val,&ipv6addr)>0)
147	{
148		/* write address type */
149		WRITE_INT32(fp,AF_INET6);
150		/* write the address length */
151		WRITE_INT32(fp,sizeof(struct in6_addr));
152		/* write the address itself (in network byte order) */
153		WRITE_TYPE(fp,ipv6addr,struct in6_addr);
154	}
155	else
156	{
157		/* failure, log but write simple invalid address
158			 (otherwise the address list is messed up) */
159		/* TODO: have error message in correct format */
160		Debug(LDAP_DEBUG_ANY,"nssov: unparseable address: %s\n",addr->bv_val,0,0);
161		/* write an illegal address type */
162		WRITE_INT32(fp,-1);
163		/* write an empty address */
164		WRITE_INT32(fp,0);
165	}
166	/* we're done */
167	return 0;
168}
169
170int read_address(TFILE *fp,char *addr,int *addrlen,int *af)
171{
172	int32_t tmpint32;
173	int len;
174	/* read address family */
175	READ_INT32(fp,*af);
176	if ((*af!=AF_INET)&&(*af!=AF_INET6))
177	{
178		Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d\n",*af,0,0);
179		return -1;
180	}
181	/* read address length */
182	READ_INT32(fp,len);
183	if ((len>*addrlen)||(len<=0))
184	{
185		Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d\n",len,0,0);
186		return -1;
187	}
188	*addrlen=len;
189	/* read address */
190	READ(fp,addr,len);
191	/* we're done */
192	return 0;
193}
194
195int nssov_escape(struct berval *src,struct berval *dst)
196{
197	size_t pos=0;
198	int i;
199	/* go over all characters in source string */
200	for (i=0;i<src->bv_len;i++)
201	{
202		/* check if char will fit */
203		if (pos>=(dst->bv_len-4))
204			return -1;
205		/* do escaping for some characters */
206		switch (src->bv_val[i])
207		{
208			case '*':
209				strcpy(dst->bv_val+pos,"\\2a");
210				pos+=3;
211				break;
212			case '(':
213				strcpy(dst->bv_val+pos,"\\28");
214				pos+=3;
215				break;
216			case ')':
217				strcpy(dst->bv_val+pos,"\\29");
218				pos+=3;
219				break;
220			case '\\':
221				strcpy(dst->bv_val+pos,"\\5c");
222				pos+=3;
223				break;
224			default:
225				/* just copy character */
226				dst->bv_val[pos++]=src->bv_val[i];
227				break;
228		}
229	}
230	/* terminate destination string */
231	dst->bv_val[pos]='\0';
232	dst->bv_len = pos;
233	return 0;
234}
235
236/* read the version information and action from the stream
237   this function returns the read action in location pointer to by action */
238static int read_header(TFILE *fp,int32_t *action)
239{
240  int32_t tmpint32;
241  /* read the protocol version */
242  READ_TYPE(fp,tmpint32,int32_t);
243  if (tmpint32 != (int32_t)NSLCD_VERSION)
244  {
245    Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32,0,0);
246    return -1;
247  }
248  /* read the request type */
249  READ(fp,action,sizeof(int32_t));
250  return 0;
251}
252
253/* read a request message, returns <0 in case of errors,
254   this function closes the socket */
255static void handleconnection(nssov_info *ni,int sock,Operation *op)
256{
257  TFILE *fp;
258  int32_t action;
259  struct timeval readtimeout,writetimeout;
260  uid_t uid;
261  gid_t gid;
262  char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")];
263  char peerbuf[8];
264  struct berval peerbv = { sizeof(peerbuf), peerbuf };
265
266  /* log connection */
267  if (LUTIL_GETPEEREID(sock,&uid,&gid,&peerbv))
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 */
347static 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
403static 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
418static 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
428enum {
429	NSS_SSD=1,
430	NSS_MAP,
431	NSS_PAM,
432	NSS_PAMGROUP,
433	NSS_PAMSESS
434};
435
436static ConfigDriver nss_cf_gen;
437
438static 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
503static 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
516static int
517nss_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
703static int
704nssov_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
738static int
739nssov_db_destroy(
740	BackendDB *be,
741	ConfigReply *cr )
742{
743}
744
745static int
746nssov_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
876static int
877nssov_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	if ( slapMode & SLAP_SERVER_MODE ) {
885		/* close socket if it's still in use */
886		if (ni->ni_socket >= 0);
887		{
888			if (close(ni->ni_socket))
889				Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",strerror(errno),0,0);
890			ni->ni_socket = -1;
891		}
892		/* remove existing named socket */
893		if (unlink(NSLCD_SOCKET)<0)
894		{
895			Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s",
896				strerror(errno),0,0);
897		}
898	}
899}
900
901static slap_overinst nssov;
902
903int
904nssov_initialize( void )
905{
906	int rc;
907
908	nssov.on_bi.bi_type = "nssov";
909	nssov.on_bi.bi_db_init = nssov_db_init;
910	nssov.on_bi.bi_db_destroy = nssov_db_destroy;
911	nssov.on_bi.bi_db_open = nssov_db_open;
912	nssov.on_bi.bi_db_close = nssov_db_close;
913
914	nssov.on_bi.bi_cf_ocs = nssocs;
915
916	rc = config_register_schema( nsscfg, nssocs );
917	if ( rc ) return rc;
918
919	return overlay_register(&nssov);
920}
921
922#if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC
923int
924init_module( int argc, char *argv[] )
925{
926	return nssov_initialize();
927}
928#endif
929