1 /* group.c - group lookup routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-2021 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 */
42 static struct berval group_filter = BER_BVC("(objectClass=posixGroup)");
43 
44 /* the attributes to request with searches */
45 static 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 */
61 static struct berval default_group_userPassword     = BER_BVC("*"); /* unmatchable */
62 
63 NSSOV_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 */
mkfilter_group_bymember(nssov_group_cbp * cbp,struct berval * buf)73 static 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 
NSSOV_INIT(group)97 NSSOV_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 */
111 static 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 
write_group(nssov_group_cbp * cbp,Entry * entry)140 static 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 = 0;
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_INT32(cbp->fp,gid);
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 
273 NSSOV_CB(group)
274 
275 NSSOV_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 
296 NSSOV_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_INT32(fp,gid);
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 
314 NSSOV_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 
335 NSSOV_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