xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/nssov/group.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: group.c,v 1.3 2021/08/14 16:14:52 christos Exp $	*/
2 
3 /* group.c - group lookup routines */
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-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 */
44 static struct berval group_filter = BER_BVC("(objectClass=posixGroup)");
45 
46 /* the attributes to request with searches */
47 static 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 */
63 static struct berval default_group_userPassword     = BER_BVC("*"); /* unmatchable */
64 
65 NSSOV_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 */
mkfilter_group_bymember(nssov_group_cbp * cbp,struct berval * buf)75 static 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 
NSSOV_INIT(group)99 NSSOV_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 */
113 static 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 
write_group(nssov_group_cbp * cbp,Entry * entry)142 static 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 = 0;
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 );
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 );
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 );
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_INT32(cbp->fp,gid);
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 
275 NSSOV_CB(group)
276 
277 NSSOV_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);
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);,
294 	NSLCD_ACTION_GROUP_BYNAME,
295 	nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter)
296 )
297 
298 NSSOV_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_INT32(fp,gid);
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);,
312 	NSLCD_ACTION_GROUP_BYGID,
313 	nssov_filter_byid(cbp.mi,GID_KEY,&cbp.gidnum,&filter)
314 )
315 
316 NSSOV_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);
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);,
333 	NSLCD_ACTION_GROUP_BYMEMBER,
334 	mkfilter_group_bymember(&cbp,&filter)
335 )
336 
337 NSSOV_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");,
346 	NSLCD_ACTION_GROUP_ALL,
347 	(filter=cbp.mi->mi_filter,0)
348 )
349