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