1/*	$NetBSD$	*/
2
3/* group.c - group lookup routines */
4/* OpenLDAP: pkg/ldap/contrib/slapd-modules/nssov/group.c,v 1.1.2.6 2010/04/15 21:32:56 quanah Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2008-2010 The OpenLDAP Foundation.
8 * Portions Copyright 2008-2009 by Howard Chu, Symas Corp.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted only as authorized by the OpenLDAP
13 * Public License.
14 *
15 * A copy of this license is available in the file LICENSE in the
16 * top-level directory of the distribution or, alternatively, at
17 * <http://www.OpenLDAP.org/license.html>.
18 */
19/* ACKNOWLEDGEMENTS:
20 * This code references portions of the nss-ldapd package
21 * written by Arthur de Jong. The nss-ldapd code was forked
22 * from the nss-ldap library written by Luke Howard.
23 */
24
25#include "nssov.h"
26
27/* for gid_t */
28#include <grp.h>
29
30/* ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL
31 *   DESC 'Abstraction of a group of accounts'
32 *   MUST ( cn $ gidNumber )
33 *   MAY ( userPassword $ memberUid $ description ) )
34 *
35 * apart from that the above the uniqueMember attributes may be
36 * supported in a coming release (they map to DNs, which is an extra
37 * lookup step)
38 *
39 * using nested groups (groups that are member of a group) is currently
40 * not supported, this may be added in a later release
41 */
42
43/* the basic search filter for searches */
44static struct berval group_filter = BER_BVC("(objectClass=posixGroup)");
45
46/* the attributes to request with searches */
47static struct berval group_keys[] = {
48	BER_BVC("cn"),
49	BER_BVC("userPassword"),
50	BER_BVC("gidNumber"),
51	BER_BVC("memberUid"),
52	BER_BVC("uniqueMember"),
53	BER_BVNULL
54};
55
56#define	CN_KEY	0
57#define	PWD_KEY	1
58#define	GID_KEY	2
59#define	UID_KEY	3
60#define	MEM_KEY	4
61
62/* default values for attributes */
63static struct berval default_group_userPassword     = BER_BVC("*"); /* unmatchable */
64
65NSSOV_CBPRIV(group,
66	nssov_info *ni;
67	char buf[256];
68	struct berval name;
69	struct berval gidnum;
70	struct berval user;
71	int wantmembers;);
72
73/* create a search filter for searching a group entry
74	 by member uid, return -1 on errors */
75static int mkfilter_group_bymember(nssov_group_cbp *cbp,struct berval *buf)
76{
77	struct berval dn;
78	/* try to translate uid to DN */
79	nssov_uid2dn(cbp->op,cbp->ni,&cbp->user,&dn);
80	if (BER_BVISNULL(&dn)) {
81		if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + 6 >
82			buf->bv_len )
83			return -1;
84		buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
85			cbp->mi->mi_filter.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,
86			cbp->user.bv_val );
87	} else { /* also lookup using user DN */
88		if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len +
89			dn.bv_len + cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_len + 12 > buf->bv_len )
90			return -1;
91		buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(|(%s=%s)(%s=%s)))",
92			cbp->mi->mi_filter.bv_val,
93			cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, cbp->user.bv_val,
94			cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_val, dn.bv_val );
95	}
96	return 0;
97}
98
99NSSOV_INIT(group)
100
101/*
102	 Checks to see if the specified name is a valid group name.
103
104	 This test is based on the definition from POSIX (IEEE Std 1003.1, 2004,
105	 3.189 Group Name and 3.276 Portable Filename Character Set):
106	 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_189
107	 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
108
109	 The standard defines group names valid if they only contain characters from
110	 the set [A-Za-z0-9._-] where the hyphen should not be used as first
111	 character.
112*/
113static int isvalidgroupname(struct berval *name)
114{
115	int i;
116
117	if ( !name->bv_val || !name->bv_len )
118		return 0;
119	/* check first character */
120	if ( ! ( (name->bv_val[0]>='A' && name->bv_val[0] <= 'Z') ||
121					 (name->bv_val[0]>='a' && name->bv_val[0] <= 'z') ||
122					 (name->bv_val[0]>='0' && name->bv_val[0] <= '9') ||
123					 name->bv_val[0]=='.' || name->bv_val[0]=='_' ) )
124		return 0;
125	/* check other characters */
126	for (i=1;i<name->bv_len;i++)
127	{
128#ifndef STRICT_GROUPS
129		/* allow spaces too */
130		if (name->bv_val[i] == ' ') continue;
131#endif
132		if ( ! ( (name->bv_val[i]>='A' && name->bv_val[i] <= 'Z') ||
133						 (name->bv_val[i]>='a' && name->bv_val[i] <= 'z') ||
134						 (name->bv_val[i]>='0' && name->bv_val[i] <= '9') ||
135						 name->bv_val[i]=='.' || name->bv_val[i]=='_' || name->bv_val[i]=='-') )
136			return 0;
137	}
138	/* no test failed so it must be good */
139	return -1;
140}
141
142static int write_group(nssov_group_cbp *cbp,Entry *entry)
143{
144	struct berval tmparr[2], tmpgid[2];
145	struct berval *names,*gids,*members;
146	struct berval passwd = {0};
147	Attribute *a;
148	int i,j,nummembers,rc;
149
150	/* get group name (cn) */
151	if (BER_BVISNULL(&cbp->name))
152	{
153		a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
154		if ( !a )
155		{
156			Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
157					entry->e_name.bv_val, cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val,0);
158			return 0;
159		}
160		names = a->a_vals;
161	}
162	else
163	{
164		names=tmparr;
165		names[0]=cbp->name;
166		BER_BVZERO(&names[1]);
167	}
168	/* get the group id(s) */
169	if (BER_BVISNULL(&cbp->gidnum))
170	{
171		a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GID_KEY].an_desc);
172		if ( !a )
173		{
174			Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
175					entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,0);
176			return 0;
177		}
178		gids = a->a_vals;
179	}
180	else
181	{
182		gids=tmpgid;
183		gids[0]=cbp->gidnum;
184		BER_BVZERO(&gids[1]);
185	}
186	/* get group passwd (userPassword) (use only first entry) */
187	a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
188	if (a)
189		get_userpassword(&a->a_vals[0], &passwd);
190	if (BER_BVISNULL(&passwd))
191		passwd=default_group_userPassword;
192	/* get group members (memberUid&uniqueMember) */
193	if (cbp->wantmembers) {
194		Attribute *b;
195		i = 0; j = 0;
196		a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
197		b = attr_find(entry->e_attrs, cbp->mi->mi_attrs[MEM_KEY].an_desc);
198		if ( a )
199			i += a->a_numvals;
200		if ( b )
201			i += b->a_numvals;
202		if ( i ) {
203			members = cbp->op->o_tmpalloc( (i+1) * sizeof(struct berval), cbp->op->o_tmpmemctx );
204
205			if ( a ) {
206				for (i=0; i<a->a_numvals; i++) {
207					if (isvalidusername(&a->a_vals[i])) {
208						ber_dupbv_x(&members[j],&a->a_vals[i],cbp->op->o_tmpmemctx);
209						j++;
210					}
211				}
212			}
213			a = b;
214			if ( a ) {
215				for (i=0; i<a->a_numvals; i++) {
216					if (nssov_dn2uid(cbp->op,cbp->ni,&a->a_nvals[i],&members[j]))
217						j++;
218				}
219			}
220			nummembers = j;
221			BER_BVZERO(&members[j]);
222		} else {
223			members=NULL;
224			nummembers = 0;
225		}
226
227	} else {
228		members=NULL;
229		nummembers = 0;
230	}
231	/* write entries for all names and gids */
232	for (i=0;!BER_BVISNULL(&names[i]);i++)
233	{
234		if (!isvalidgroupname(&names[i]))
235		{
236			Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains invalid group name: \"%s\"\n",
237													entry->e_name.bv_val,names[i].bv_val,0);
238		}
239		else
240		{
241			for (j=0;!BER_BVISNULL(&gids[j]);j++)
242			{
243				char *tmp;
244				int tmpint32;
245				gid_t gid;
246				gid = strtol(gids[j].bv_val, &tmp, 0);
247				if ( *tmp ) {
248					Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains non-numeric %s value: \"%s\"\n",
249						entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,
250						names[i].bv_val);
251					continue;
252				}
253				WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
254				WRITE_BERVAL(cbp->fp,&names[i]);
255				WRITE_BERVAL(cbp->fp,&passwd);
256				WRITE_TYPE(cbp->fp,gid,gid_t);
257				/* write a list of values */
258				WRITE_INT32(cbp->fp,nummembers);
259				if (nummembers)
260				{
261					int k;
262					for (k=0;k<nummembers;k++) {
263						WRITE_BERVAL(cbp->fp,&members[k]);
264					}
265				}
266			}
267		}
268	}
269	/* free and return */
270	if (members!=NULL)
271		ber_bvarray_free_x( members, cbp->op->o_tmpmemctx );
272	return rc;
273}
274
275NSSOV_CB(group)
276
277NSSOV_HANDLE(
278	group,byname,
279	char fbuf[1024];
280	struct berval filter = {sizeof(fbuf)};
281	filter.bv_val = fbuf;
282	READ_STRING(fp,cbp.buf);
283	cbp.name.bv_len = tmpint32;
284	cbp.name.bv_val = cbp.buf;
285	if (!isvalidgroupname(&cbp.name)) {
286		Debug(LDAP_DEBUG_ANY,"nssov_group_byname(%s): invalid group name\n",cbp.name.bv_val,0,0);
287		return -1;
288	}
289	cbp.wantmembers = 1;
290	cbp.ni = ni;
291	BER_BVZERO(&cbp.gidnum);
292	BER_BVZERO(&cbp.user);,
293	Debug(LDAP_DEBUG_TRACE,"nslcd_group_byname(%s)\n",cbp.name.bv_val,0,0);,
294	NSLCD_ACTION_GROUP_BYNAME,
295	nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter)
296)
297
298NSSOV_HANDLE(
299	group,bygid,
300	gid_t gid;
301	char fbuf[1024];
302	struct berval filter = {sizeof(fbuf)};
303	filter.bv_val = fbuf;
304	READ_TYPE(fp,gid,gid_t);
305	cbp.gidnum.bv_val = cbp.buf;
306	cbp.gidnum.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",gid);
307	cbp.wantmembers = 1;
308	cbp.ni = ni;
309	BER_BVZERO(&cbp.name);
310	BER_BVZERO(&cbp.user);,
311	Debug(LDAP_DEBUG_TRACE,"nssov_group_bygid(%s)\n",cbp.gidnum.bv_val,0,0);,
312	NSLCD_ACTION_GROUP_BYGID,
313	nssov_filter_byid(cbp.mi,GID_KEY,&cbp.gidnum,&filter)
314)
315
316NSSOV_HANDLE(
317	group,bymember,
318	char fbuf[1024];
319	struct berval filter = {sizeof(fbuf)};
320	filter.bv_val = fbuf;
321	READ_STRING(fp,cbp.buf);
322	cbp.user.bv_len = tmpint32;
323	cbp.user.bv_val = cbp.buf;
324	if (!isvalidusername(&cbp.user)) {
325		Debug(LDAP_DEBUG_ANY,"nssov_group_bymember(%s): invalid user name\n",cbp.user.bv_val,0,0);
326		return -1;
327	}
328	cbp.wantmembers = 0;
329	cbp.ni = ni;
330	BER_BVZERO(&cbp.name);
331	BER_BVZERO(&cbp.gidnum);,
332	Debug(LDAP_DEBUG_TRACE,"nssov_group_bymember(%s)\n",cbp.user.bv_val,0,0);,
333	NSLCD_ACTION_GROUP_BYMEMBER,
334	mkfilter_group_bymember(&cbp,&filter)
335)
336
337NSSOV_HANDLE(
338	group,all,
339	struct berval filter;
340	/* no parameters to read */
341	cbp.wantmembers = 1;
342	cbp.ni = ni;
343	BER_BVZERO(&cbp.name);
344	BER_BVZERO(&cbp.gidnum);,
345	Debug(LDAP_DEBUG_TRACE,"nssov_group_all()\n",0,0,0);,
346	NSLCD_ACTION_GROUP_ALL,
347	(filter=cbp.mi->mi_filter,0)
348)
349