1/*	$NetBSD: nssov.c,v 1.3 2021/08/14 16:14:52 christos 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-2021 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 "slap-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
41AttributeDescription *nssov_pam_host_ad;
42AttributeDescription *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 */
51void 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 */
77int 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 */
96int 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
108void 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 */
134int 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(fp,&ipv4addr,sizeof(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(fp,&ipv6addr,sizeof(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: unparsable address: %s\n",addr->bv_val );
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
173int 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 );
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 );
189		return -1;
190	}
191	*addrlen=len;
192	/* read address */
193	READ(fp,addr,len);
194	/* we're done */
195	return 0;
196}
197
198int 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 */
241static int read_header(TFILE *fp,int32_t *action)
242{
243  int32_t tmpint32;
244  /* read the protocol version */
245  READ_INT32(fp,tmpint32);
246  if (tmpint32 != (int32_t)NSLCD_VERSION)
247  {
248    Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32 );
249    return -1;
250  }
251  /* read the request type */
252  READ_INT32(fp,*action);
253  return 0;
254}
255
256int nssov_config(nssov_info *ni,TFILE *fp,Operation *op)
257{
258	int opt;
259	int32_t tmpint32;
260
261	READ_INT32(fp,opt);
262
263	Debug(LDAP_DEBUG_TRACE, "nssov_config (%d)\n",opt );
264
265	WRITE_INT32(fp,NSLCD_VERSION);
266	WRITE_INT32(fp,NSLCD_ACTION_CONFIG_GET);
267	WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
268
269	switch (opt) {
270	case NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE:
271		/* request for pam password_prohibit_message */
272		/* nssov_pam prohibits password  */
273		if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
274			Debug(LDAP_DEBUG_TRACE,"nssov_config(): %s (%s)\n",
275				"password_prohibit_message",
276				ni->ni_pam_password_prohibit_message.bv_val );
277			WRITE_STRING(fp,ni->ni_pam_password_prohibit_message.bv_val);
278		}
279	default:
280		/* all other config options are ignored */
281		break;
282	}
283
284	WRITE_INT32(fp,NSLCD_RESULT_END);
285	return 0;
286}
287
288
289/* read a request message, returns <0 in case of errors,
290   this function closes the socket */
291static void handleconnection(nssov_info *ni,int sock,Operation *op)
292{
293  TFILE *fp;
294  int32_t action;
295  int readtimeout,writetimeout;
296  uid_t uid;
297  gid_t gid;
298  char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")];
299  char peerbuf[8];
300  struct berval peerbv = { sizeof(peerbuf), peerbuf };
301
302  /* log connection */
303  if (LUTIL_GETPEEREID(sock,&uid,&gid,&peerbv)) {
304    char ebuf[128];
305    int saved_errno = errno;
306    Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s\n",
307                      AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
308  } else {
309    Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d\n",
310                      (int)uid,(int)gid );
311  }
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    char ebuf[128];
332    int saved_errno = errno;
333    Debug( LDAP_DEBUG_ANY,"nssov: cannot create stream for writing: %s",
334                      AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
335    (void)close(sock);
336    return;
337  }
338  /* read request */
339  if (read_header(fp,&action))
340  {
341    (void)tio_close(fp);
342    return;
343  }
344  /* handle request */
345  switch (action)
346  {
347    case NSLCD_ACTION_ALIAS_BYNAME:     (void)nssov_alias_byname(ni,fp,op); break;
348    case NSLCD_ACTION_ALIAS_ALL:        (void)nssov_alias_all(ni,fp,op); break;
349    case NSLCD_ACTION_ETHER_BYNAME:     (void)nssov_ether_byname(ni,fp,op); break;
350    case NSLCD_ACTION_ETHER_BYETHER:    (void)nssov_ether_byether(ni,fp,op); break;
351    case NSLCD_ACTION_ETHER_ALL:        (void)nssov_ether_all(ni,fp,op); break;
352    case NSLCD_ACTION_GROUP_BYNAME:     (void)nssov_group_byname(ni,fp,op); break;
353    case NSLCD_ACTION_GROUP_BYGID:      (void)nssov_group_bygid(ni,fp,op); break;
354    case NSLCD_ACTION_GROUP_BYMEMBER:   (void)nssov_group_bymember(ni,fp,op); break;
355    case NSLCD_ACTION_GROUP_ALL:        (void)nssov_group_all(ni,fp,op); break;
356    case NSLCD_ACTION_HOST_BYNAME:      (void)nssov_host_byname(ni,fp,op); break;
357    case NSLCD_ACTION_HOST_BYADDR:      (void)nssov_host_byaddr(ni,fp,op); break;
358    case NSLCD_ACTION_HOST_ALL:         (void)nssov_host_all(ni,fp,op); break;
359    case NSLCD_ACTION_NETGROUP_BYNAME:  (void)nssov_netgroup_byname(ni,fp,op); break;
360    case NSLCD_ACTION_NETWORK_BYNAME:   (void)nssov_network_byname(ni,fp,op); break;
361    case NSLCD_ACTION_NETWORK_BYADDR:   (void)nssov_network_byaddr(ni,fp,op); break;
362    case NSLCD_ACTION_NETWORK_ALL:      (void)nssov_network_all(ni,fp,op); break;
363    case NSLCD_ACTION_PASSWD_BYNAME:    (void)nssov_passwd_byname(ni,fp,op); break;
364    case NSLCD_ACTION_PASSWD_BYUID:     (void)nssov_passwd_byuid(ni,fp,op); break;
365    case NSLCD_ACTION_PASSWD_ALL:       (void)nssov_passwd_all(ni,fp,op); break;
366    case NSLCD_ACTION_PROTOCOL_BYNAME:  (void)nssov_protocol_byname(ni,fp,op); break;
367    case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nssov_protocol_bynumber(ni,fp,op); break;
368    case NSLCD_ACTION_PROTOCOL_ALL:     (void)nssov_protocol_all(ni,fp,op); break;
369    case NSLCD_ACTION_RPC_BYNAME:       (void)nssov_rpc_byname(ni,fp,op); break;
370    case NSLCD_ACTION_RPC_BYNUMBER:     (void)nssov_rpc_bynumber(ni,fp,op); break;
371    case NSLCD_ACTION_RPC_ALL:          (void)nssov_rpc_all(ni,fp,op); break;
372    case NSLCD_ACTION_SERVICE_BYNAME:   (void)nssov_service_byname(ni,fp,op); break;
373    case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nssov_service_bynumber(ni,fp,op); break;
374    case NSLCD_ACTION_SERVICE_ALL:      (void)nssov_service_all(ni,fp,op); break;
375    case NSLCD_ACTION_SHADOW_BYNAME:    if (uid==0) (void)nssov_shadow_byname(ni,fp,op); break;
376    case NSLCD_ACTION_SHADOW_ALL:       if (uid==0) (void)nssov_shadow_all(ni,fp,op); break;
377	case NSLCD_ACTION_PAM_AUTHC:		(void)pam_authc(ni,fp,op,uid); break;
378	case NSLCD_ACTION_PAM_AUTHZ:		(void)pam_authz(ni,fp,op); break;
379	case NSLCD_ACTION_PAM_SESS_O:		if (uid==0) (void)pam_sess_o(ni,fp,op); break;
380	case NSLCD_ACTION_PAM_SESS_C:		if (uid==0) (void)pam_sess_c(ni,fp,op); break;
381	case NSLCD_ACTION_PAM_PWMOD:		(void)pam_pwmod(ni,fp,op,uid); break;
382	case NSLCD_ACTION_CONFIG_GET:			(void)nssov_config(ni,fp,op); break;
383    default:
384      Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action );
385      break;
386  }
387  /* we're done with the request */
388  (void)tio_close(fp);
389  return;
390}
391
392/* accept a connection on the socket */
393static void *acceptconn(void *ctx, void *arg)
394{
395	nssov_info *ni = arg;
396	Connection conn = {0};
397	OperationBuffer opbuf;
398	Operation *op;
399	int csock;
400
401	if ( slapd_shutdown )
402		return NULL;
403
404	{
405		struct sockaddr_storage addr;
406		socklen_t alen;
407		int j;
408
409		/* accept a new connection */
410		alen=(socklen_t)sizeof(struct sockaddr_storage);
411		csock=accept(ni->ni_socket,(struct sockaddr *)&addr,&alen);
412		connection_client_enable(ni->ni_conn);
413		if (csock<0)
414		{
415			char ebuf[128];
416			int saved_errno = errno;
417			if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK))
418			{
419				Debug( LDAP_DEBUG_TRACE,"nssov: accept() failed (ignored): %s",
420					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
421				return NULL;
422			}
423			Debug( LDAP_DEBUG_ANY,"nssov: accept() failed: %s",
424				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
425			return NULL;
426		}
427		/* make sure O_NONBLOCK is not inherited */
428		if ((j=fcntl(csock,F_GETFL,0))<0)
429		{
430			char ebuf[128];
431			int saved_errno = errno;
432			Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_GETFL) failed: %s",
433				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
434			if (close(csock)) {
435				saved_errno = errno;
436				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
437					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
438			}
439			return NULL;
440		}
441		if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0)
442		{
443			char ebuf[128];
444			int saved_errno = errno;
445			Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,~O_NONBLOCK) failed: %s",
446				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
447			if (close(csock)) {
448				saved_errno = errno;
449				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
450					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
451			}
452			return NULL;
453		}
454	}
455	connection_fake_init( &conn, &opbuf, ctx );
456	op=&opbuf.ob_op;
457	conn.c_ssf = conn.c_transport_ssf = local_ssf;
458	op->o_bd = ni->ni_db;
459	op->o_tag = LDAP_REQ_SEARCH;
460
461	/* handle the connection */
462	handleconnection(ni,csock,op);
463
464	return NULL;
465}
466
467static slap_verbmasks nss_svcs[] = {
468	{ BER_BVC("aliases"), NM_alias },
469	{ BER_BVC("ethers"), NM_ether },
470	{ BER_BVC("group"), NM_group },
471	{ BER_BVC("hosts"), NM_host },
472	{ BER_BVC("netgroup"), NM_netgroup },
473	{ BER_BVC("networks"), NM_network },
474	{ BER_BVC("passwd"), NM_passwd },
475	{ BER_BVC("protocols"), NM_protocol },
476	{ BER_BVC("rpc"), NM_rpc },
477	{ BER_BVC("services"), NM_service },
478	{ BER_BVC("shadow"), NM_shadow },
479	{ BER_BVNULL, 0 }
480};
481
482static slap_verbmasks pam_opts[] = {
483	{ BER_BVC("userhost"), NI_PAM_USERHOST },
484	{ BER_BVC("userservice"), NI_PAM_USERSVC },
485	{ BER_BVC("usergroup"), NI_PAM_USERGRP },
486	{ BER_BVC("hostservice"), NI_PAM_HOSTSVC },
487	{ BER_BVC("authz2dn"), NI_PAM_SASL2DN },
488	{ BER_BVC("uid2dn"), NI_PAM_UID2DN },
489	{ BER_BVNULL, 0 }
490};
491
492enum {
493	NSS_SSD=1,
494	NSS_MAP,
495	NSS_PAM,
496	NSS_PAMGROUP,
497	NSS_PAMSESS
498};
499
500static ConfigDriver nss_cf_gen;
501
502static ConfigTable nsscfg[] = {
503	{ "nssov-ssd", "service> <url", 3, 3, 0, ARG_MAGIC|NSS_SSD,
504		nss_cf_gen, "(OLcfgCtAt:3.1 NAME 'olcNssSsd' "
505			"DESC 'URL for searches in a given service' "
506			"EQUALITY caseIgnoreMatch "
507			"SYNTAX OMsDirectoryString )", NULL, NULL },
508	{ "nssov-map", "service> <orig> <new", 4, 4, 0, ARG_MAGIC|NSS_MAP,
509		nss_cf_gen, "(OLcfgCtAt:3.2 NAME 'olcNssMap' "
510			"DESC 'Map <service> lookups of <orig> attr to <new> attr' "
511			"EQUALITY caseIgnoreMatch "
512			"SYNTAX OMsDirectoryString )", NULL, NULL },
513	{ "nssov-pam", "options", 2, 0, 0, ARG_MAGIC|NSS_PAM,
514		nss_cf_gen, "(OLcfgCtAt:3.3 NAME 'olcNssPam' "
515			"DESC 'PAM authentication and authorization options' "
516			"EQUALITY caseIgnoreMatch "
517			"SYNTAX OMsDirectoryString )", NULL, NULL },
518	{ "nssov-pam-defhost", "hostname", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
519		(void *)offsetof(struct nssov_info, ni_pam_defhost),
520		"(OLcfgCtAt:3.4 NAME 'olcNssPamDefHost' "
521			"DESC 'Default hostname for service checks' "
522			"EQUALITY caseIgnoreMatch "
523			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
524	{ "nssov-pam-group-dn", "DN", 2, 2, 0, ARG_MAGIC|ARG_DN|NSS_PAMGROUP,
525		nss_cf_gen, "(OLcfgCtAt:3.5 NAME 'olcNssPamGroupDN' "
526			"DESC 'DN of group in which membership is required' "
527			"EQUALITY distinguishedNameMatch "
528			"SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
529	{ "nssov-pam-group-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
530		(void *)offsetof(struct nssov_info, ni_pam_group_ad),
531		"(OLcfgCtAt:3.6 NAME 'olcNssPamGroupAD' "
532			"DESC 'Member attribute to use for group check' "
533			"EQUALITY caseIgnoreMatch "
534			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
535	{ "nssov-pam-min-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
536		(void *)offsetof(struct nssov_info, ni_pam_min_uid),
537		"(OLcfgCtAt:3.7 NAME 'olcNssPamMinUid' "
538			"DESC 'Minimum UID allowed to login' "
539			"EQUALITY integerMatch "
540			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
541	{ "nssov-pam-max-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
542		(void *)offsetof(struct nssov_info, ni_pam_max_uid),
543		"(OLcfgCtAt:3.8 NAME 'olcNssPamMaxUid' "
544			"DESC 'Maximum UID allowed to login' "
545			"EQUALITY integerMatch "
546			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
547	{ "nssov-pam-template-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
548		(void *)offsetof(struct nssov_info, ni_pam_template_ad),
549		"(OLcfgCtAt:3.9 NAME 'olcNssPamTemplateAD' "
550			"DESC 'Attribute to use for template login name' "
551			"EQUALITY caseIgnoreMatch "
552			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
553	{ "nssov-pam-template", "name", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
554		(void *)offsetof(struct nssov_info, ni_pam_template),
555		"(OLcfgCtAt:3.10 NAME 'olcNssPamTemplate' "
556			"DESC 'Default template login name' "
557			"EQUALITY caseIgnoreMatch "
558			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
559	{ "nssov-pam-session", "service", 2, 2, 0, ARG_MAGIC|NSS_PAMSESS,
560		nss_cf_gen, "(OLcfgCtAt:3.11 NAME 'olcNssPamSession' "
561			"DESC 'Services for which sessions will be recorded' "
562			"EQUALITY caseIgnoreMatch "
563			"SYNTAX OMsDirectoryString )", NULL, NULL },
564	{ "nssov-pam-password-prohibit-message",
565		"password_prohibit_message", 2, 2, 0,
566		ARG_OFFSET|ARG_BERVAL,
567		(void *)offsetof(struct nssov_info, ni_pam_password_prohibit_message),
568		"(OLcfgCtAt:3.12 NAME 'olcNssPamPwdProhibitMsg' "
569			"DESC 'Prohibit password modification message' "
570			"EQUALITY caseIgnoreMatch "
571			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
572	{ "nssov-pam-pwdmgr-dn",
573		"pwdmgr_dn", 2, 2, 0,
574		ARG_OFFSET|ARG_BERVAL,
575		(void *)offsetof(struct nssov_info, ni_pam_pwdmgr_dn),
576		"(OLcfgCtAt:3.13 NAME 'olcPamPwdmgrDn' "
577			"DESC 'Password Manager DN' "
578			"EQUALITY distinguishedNameMatch "
579			"SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
580	{ "nssov-pam-pwdmgr-pwd",
581		"pwdmgr_pwd", 2, 2, 0,
582		ARG_OFFSET|ARG_BERVAL,
583		(void *)offsetof(struct nssov_info, ni_pam_pwdmgr_pwd),
584		"(OLcfgCtAt:3.14 NAME 'olcPamPwdmgrPwd' "
585			"DESC 'Password Manager Pwd' "
586			"EQUALITY octetStringMatch "
587			"SYNTAX OMsOctetString SINGLE-VALUE )", NULL, NULL },
588	{ NULL, NULL, 0,0,0, ARG_IGNORED }
589};
590
591static ConfigOCs nssocs[] = {
592	{ "( OLcfgCtOc:3.1 "
593		"NAME 'olcNssOvConfig' "
594		"DESC 'NSS lookup configuration' "
595		"SUP olcOverlayConfig "
596		"MAY ( olcNssSsd $ olcNssMap $ olcNssPam $ olcNssPamDefHost $ "
597			"olcNssPamGroupDN $ olcNssPamGroupAD $ "
598			"olcNssPamMinUid $ olcNssPamMaxUid $ olcNssPamSession $ "
599			"olcNssPamTemplateAD $ olcNssPamTemplate ) )",
600		Cft_Overlay, nsscfg },
601	{ NULL, 0, NULL }
602};
603
604static int
605nss_cf_gen(ConfigArgs *c)
606{
607	slap_overinst *on = (slap_overinst *)c->bi;
608	nssov_info *ni = on->on_bi.bi_private;
609	nssov_mapinfo *mi;
610	int i, j, rc = 0;
611	slap_mask_t m;
612
613	if ( c->op == SLAP_CONFIG_EMIT ) {
614		switch(c->type) {
615		case NSS_SSD:
616			rc = 1;
617			for (i=NM_alias;i<NM_NONE;i++) {
618				struct berval scope;
619				struct berval ssd;
620				struct berval base;
621
622				mi = &ni->ni_maps[i];
623
624				/* ignore all-default services */
625				if ( mi->mi_scope == LDAP_SCOPE_DEFAULT &&
626					bvmatch( &mi->mi_filter, &mi->mi_filter0 ) &&
627					BER_BVISNULL( &mi->mi_base ))
628					continue;
629
630				if ( BER_BVISNULL( &mi->mi_base ))
631					base = ni->ni_db->be_nsuffix[0];
632				else
633					base = mi->mi_base;
634				ldap_pvt_scope2bv(mi->mi_scope == LDAP_SCOPE_DEFAULT ?
635					LDAP_SCOPE_SUBTREE : mi->mi_scope, &scope);
636				ssd.bv_len = STRLENOF(" ldap:///???") + nss_svcs[i].word.bv_len +
637					base.bv_len + scope.bv_len + mi->mi_filter.bv_len;
638				ssd.bv_val = ch_malloc( ssd.bv_len + 1 );
639				sprintf(ssd.bv_val, "%s ldap:///%s??%s?%s", nss_svcs[i].word.bv_val,
640					base.bv_val, scope.bv_val, mi->mi_filter.bv_val );
641				ber_bvarray_add( &c->rvalue_vals, &ssd );
642				rc = 0;
643			}
644			break;
645		case NSS_MAP:
646			rc = 1;
647			for (i=NM_alias;i<NM_NONE;i++) {
648
649				mi = &ni->ni_maps[i];
650				for (j=0;!BER_BVISNULL(&mi->mi_attrkeys[j]);j++) {
651					if ( ber_bvstrcasecmp(&mi->mi_attrkeys[j],
652						&mi->mi_attrs[j].an_name)) {
653						struct berval map;
654
655						map.bv_len = nss_svcs[i].word.bv_len +
656							mi->mi_attrkeys[j].bv_len +
657							mi->mi_attrs[j].an_desc->ad_cname.bv_len + 2;
658						map.bv_val = ch_malloc(map.bv_len + 1);
659						sprintf(map.bv_val, "%s %s %s", nss_svcs[i].word.bv_val,
660							mi->mi_attrkeys[j].bv_val, mi->mi_attrs[j].an_desc->ad_cname.bv_val );
661						ber_bvarray_add( &c->rvalue_vals, &map );
662						rc = 0;
663					}
664				}
665			}
666			break;
667		case NSS_PAM:
668			rc = mask_to_verbs( pam_opts, ni->ni_pam_opts, &c->rvalue_vals );
669			break;
670		case NSS_PAMGROUP:
671			if (!BER_BVISEMPTY( &ni->ni_pam_group_dn )) {
672				value_add_one( &c->rvalue_vals, &ni->ni_pam_group_dn );
673				value_add_one( &c->rvalue_nvals, &ni->ni_pam_group_dn );
674			} else {
675				rc = 1;
676			}
677			break;
678		case NSS_PAMSESS:
679			if (ni->ni_pam_sessions) {
680				ber_bvarray_dup_x( &c->rvalue_vals, ni->ni_pam_sessions, NULL );
681			} else {
682				rc = 1;
683			}
684			break;
685		}
686		return rc;
687	} else if ( c->op == LDAP_MOD_DELETE ) {
688		/* FIXME */
689		return 1;
690	}
691	switch( c->type ) {
692	case NSS_SSD: {
693		LDAPURLDesc *lud;
694
695		i = verb_to_mask(c->argv[1], nss_svcs);
696		if ( i == NM_NONE )
697			return 1;
698
699		mi = &ni->ni_maps[i];
700		rc = ldap_url_parse(c->argv[2], &lud);
701		if ( rc )
702			return 1;
703		do {
704			struct berval base;
705			/* Must be LDAP scheme */
706			if (strcasecmp(lud->lud_scheme,"ldap")) {
707				rc = 1;
708				break;
709			}
710			/* Host part, attrs, and extensions must be empty */
711			if (( lud->lud_host && *lud->lud_host ) ||
712				lud->lud_attrs || lud->lud_exts ) {
713				rc = 1;
714				break;
715			}
716			ber_str2bv( lud->lud_dn,0,0,&base);
717			rc = dnNormalize( 0,NULL,NULL,&base,&mi->mi_base,NULL);
718			if ( rc )
719				break;
720			if ( lud->lud_filter ) {
721				/* steal this */
722				ber_str2bv( lud->lud_filter,0,0,&mi->mi_filter);
723				lud->lud_filter = NULL;
724			}
725			mi->mi_scope = lud->lud_scope;
726		} while(0);
727		ldap_free_urldesc( lud );
728		}
729		break;
730	case NSS_MAP:
731		i = verb_to_mask(c->argv[1], nss_svcs);
732		if ( i == NM_NONE )
733			return 1;
734		rc = 1;
735		mi = &ni->ni_maps[i];
736		for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
737			if (!strcasecmp(c->argv[2],mi->mi_attrkeys[j].bv_val)) {
738				AttributeDescription *ad = NULL;
739				const char *text;
740				rc = slap_str2ad( c->argv[3], &ad, &text);
741				if ( rc == 0 ) {
742					mi->mi_attrs[j].an_desc = ad;
743					mi->mi_attrs[j].an_name = ad->ad_cname;
744				}
745				break;
746			}
747		}
748		break;
749	case NSS_PAM:
750		m = ni->ni_pam_opts;
751		i = verbs_to_mask(c->argc, c->argv, pam_opts, &m);
752		if (i == 0) {
753			ni->ni_pam_opts = m;
754			if ((m & NI_PAM_USERHOST) && !nssov_pam_host_ad) {
755				const char *text;
756				i = slap_str2ad("host", &nssov_pam_host_ad, &text);
757				if (i != LDAP_SUCCESS) {
758					snprintf(c->cr_msg, sizeof(c->cr_msg),
759						"nssov: host attr unknown: %s", text);
760					Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg );
761					rc = 1;
762					break;
763				}
764			}
765			if ((m & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) && !nssov_pam_svc_ad) {
766				const char *text;
767				i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
768				if (i != LDAP_SUCCESS) {
769					snprintf(c->cr_msg, sizeof(c->cr_msg),
770						"nssov: authorizedService attr unknown: %s", text);
771					Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg );
772					rc = 1;
773					break;
774				}
775			}
776		} else {
777			rc = 1;
778		}
779		break;
780	case NSS_PAMGROUP:
781		ni->ni_pam_group_dn = c->value_ndn;
782		ch_free( c->value_dn.bv_val );
783		break;
784	case NSS_PAMSESS:
785		ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
786		ber_bvarray_add( &ni->ni_pam_sessions, &c->value_bv );
787		break;
788	}
789	return rc;
790}
791
792static int
793nssov_db_init(
794	BackendDB *be,
795	ConfigReply *cr )
796{
797	slap_overinst *on = (slap_overinst *)be->bd_info;
798	nssov_info *ni;
799	int rc;
800
801	rc = nssov_pam_init();
802	if (rc) return rc;
803
804	ni = ch_calloc( 1, sizeof(nssov_info) );
805	on->on_bi.bi_private = ni;
806
807	/* set up map keys */
808	nssov_alias_init(ni);
809	nssov_ether_init(ni);
810	nssov_group_init(ni);
811	nssov_host_init(ni);
812	nssov_netgroup_init(ni);
813	nssov_network_init(ni);
814	nssov_passwd_init(ni);
815	nssov_protocol_init(ni);
816	nssov_rpc_init(ni);
817	nssov_service_init(ni);
818	nssov_shadow_init(ni);
819
820	ni->ni_db = be->bd_self;
821	ni->ni_pam_opts = NI_PAM_UID2DN;
822
823	return 0;
824}
825
826static int
827nssov_db_destroy(
828	BackendDB *be,
829	ConfigReply *cr )
830{
831	return 0;
832}
833
834static int
835nssov_db_open(
836	BackendDB *be,
837	ConfigReply *cr )
838{
839	slap_overinst *on = (slap_overinst *)be->bd_info;
840	nssov_info *ni = on->on_bi.bi_private;
841	nssov_mapinfo *mi;
842
843	int i, sock;
844	struct sockaddr_un addr;
845
846	/* Set default bases */
847	for (i=0; i<NM_NONE; i++) {
848		if ( BER_BVISNULL( &ni->ni_maps[i].mi_base )) {
849			ber_dupbv( &ni->ni_maps[i].mi_base, &be->be_nsuffix[0] );
850		}
851		if ( ni->ni_maps[i].mi_scope == LDAP_SCOPE_DEFAULT )
852			ni->ni_maps[i].mi_scope = LDAP_SCOPE_SUBTREE;
853	}
854	/* validate attribute maps */
855	mi = ni->ni_maps;
856	for ( i=0; i<NM_NONE; i++,mi++) {
857		const char *text;
858		int j;
859		for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
860			/* skip attrs we already validated */
861			if ( mi->mi_attrs[j].an_desc ) continue;
862			if ( slap_bv2ad( &mi->mi_attrs[j].an_name,
863				&mi->mi_attrs[j].an_desc, &text )) {
864				Debug(LDAP_DEBUG_ANY,"nssov: invalid attr \"%s\": %s\n",
865					mi->mi_attrs[j].an_name.bv_val, text );
866				return -1;
867			}
868		}
869		BER_BVZERO(&mi->mi_attrs[j].an_name);
870		mi->mi_attrs[j].an_desc = NULL;
871	}
872
873	/* Find host and authorizedService definitions */
874	if ((ni->ni_pam_opts & NI_PAM_USERHOST) && !nssov_pam_host_ad)
875	{
876		const char *text;
877		i = slap_str2ad("host", &nssov_pam_host_ad, &text);
878		if (i != LDAP_SUCCESS) {
879			Debug(LDAP_DEBUG_ANY,"nssov: host attr unknown: %s\n",
880				text );
881			return -1;
882		}
883	}
884	if ((ni->ni_pam_opts & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) &&
885		!nssov_pam_svc_ad)
886	{
887		const char *text;
888		i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
889		if (i != LDAP_SUCCESS) {
890			Debug(LDAP_DEBUG_ANY,"nssov: authorizedService attr unknown: %s\n",
891				text );
892			return -1;
893		}
894	}
895	if ( slapMode & SLAP_SERVER_MODE ) {
896		char ebuf[128];
897		/* make sure /var/run/nslcd exists */
898		if (mkdir(NSLCD_PATH, (mode_t) 0555)) {
899			int saved_errno = errno;
900			Debug(LDAP_DEBUG_TRACE,"nssov: mkdir(%s) failed (ignored): %s\n",
901					NSLCD_PATH, AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
902		} else {
903			Debug(LDAP_DEBUG_TRACE,"nssov: created %s\n",NSLCD_PATH );
904		}
905
906		/* create a socket */
907		if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 )
908		{
909			int saved_errno = errno;
910			Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s\n",
911					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
912			return -1;
913		}
914		/* remove existing named socket */
915		if (unlink(NSLCD_SOCKET)<0)
916		{
917			int saved_errno = errno;
918			Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s\n",
919							AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
920		}
921		/* create socket address structure */
922		memset(&addr,0,sizeof(struct sockaddr_un));
923		addr.sun_family=AF_UNIX;
924		strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path));
925		addr.sun_path[sizeof(addr.sun_path)-1]='\0';
926		/* bind to the named socket */
927		if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un)))
928		{
929			int saved_errno = errno;
930			Debug( LDAP_DEBUG_ANY,"nssov: bind() to "NSLCD_SOCKET" failed: %s",
931				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
932			if (close(sock)) {
933				saved_errno = errno;
934				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
935					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
936			}
937			return -1;
938		}
939		/* close the file descriptor on exit */
940		if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0)
941		{
942			int saved_errno = errno;
943			Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,O_NONBLOCK) failed: %s",
944				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
945			if (close(sock)) {
946				saved_errno = errno;
947				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
948					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
949			}
950			return -1;
951		}
952		/* set permissions of socket so anybody can do requests */
953		/* Note: we use chmod() here instead of fchmod() because
954			 fchmod does not work on sockets
955			 http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html
956			 http://lkml.org/lkml/2005/5/16/11 */
957		if (chmod(NSLCD_SOCKET,(mode_t)0666))
958		{
959			int saved_errno = errno;
960			Debug( LDAP_DEBUG_ANY,"nssov: chmod(0666) failed: %s",
961				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
962			if (close(sock)) {
963				saved_errno = errno;
964				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
965					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
966			}
967			return -1;
968		}
969		/* start listening for connections */
970		if (listen(sock,SOMAXCONN)<0)
971		{
972			int saved_errno = errno;
973			Debug( LDAP_DEBUG_ANY,"nssov: listen() failed: %s",
974				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
975			if (close(sock)) {
976				saved_errno = errno;
977				Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
978					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
979			}
980			return -1;
981		}
982		ni->ni_socket = sock;
983		ni->ni_conn = connection_client_setup( sock, acceptconn, ni );
984	}
985
986	return 0;
987}
988
989static int
990nssov_db_close(
991	BackendDB *be,
992	ConfigReply *cr )
993{
994	slap_overinst *on = (slap_overinst *)be->bd_info;
995	nssov_info *ni = on->on_bi.bi_private;
996
997	if ( slapMode & SLAP_SERVER_MODE ) {
998		char ebuf[128];
999		/* close socket if it's still in use */
1000		if (ni->ni_socket >= 0)
1001		{
1002			if (close(ni->ni_socket)) {
1003				int saved_errno = errno;
1004				Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",
1005					AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
1006			}
1007			ni->ni_socket = -1;
1008		}
1009		/* remove existing named socket */
1010		if (unlink(NSLCD_SOCKET)<0)
1011		{
1012			int saved_errno = errno;
1013			Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s",
1014				AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
1015		}
1016	}
1017	return 0;
1018}
1019
1020static slap_overinst nssov;
1021
1022int
1023nssov_initialize( void )
1024{
1025	int rc;
1026
1027	nssov.on_bi.bi_type = "nssov";
1028	nssov.on_bi.bi_db_init = nssov_db_init;
1029	nssov.on_bi.bi_db_destroy = nssov_db_destroy;
1030	nssov.on_bi.bi_db_open = nssov_db_open;
1031	nssov.on_bi.bi_db_close = nssov_db_close;
1032
1033	nssov.on_bi.bi_cf_ocs = nssocs;
1034
1035	rc = config_register_schema( nsscfg, nssocs );
1036	if ( rc ) return rc;
1037
1038	return overlay_register(&nssov);
1039}
1040
1041#if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC
1042int
1043init_module( int argc, char *argv[] )
1044{
1045	return nssov_initialize();
1046}
1047#endif
1048