1/* posixgroup.c */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 1998-2011 The OpenLDAP Foundation.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
10 * Public License.
11 *
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
15 */
16
17#include <portable.h>
18
19#include <ac/string.h>
20#include <slap.h>
21#include <lutil.h>
22
23/* Need dynacl... */
24
25#ifdef SLAP_DYNACL
26
27typedef struct pg_t {
28	slap_style_t		pg_style;
29	struct berval		pg_pat;
30} pg_t;
31
32static ObjectClass		*pg_posixGroup;
33static AttributeDescription	*pg_memberUid;
34static ObjectClass		*pg_posixAccount;
35static AttributeDescription	*pg_uidNumber;
36
37static int pg_dynacl_destroy( void *priv );
38
39static int
40pg_dynacl_parse(
41	const char	*fname,
42	int 		lineno,
43	const char	*opts,
44	slap_style_t	style,
45	const char	*pattern,
46	void		**privp )
47{
48	pg_t		*pg;
49	int		rc;
50	const char	*text = NULL;
51	struct berval	pat;
52
53	ber_str2bv( pattern, 0, 0, &pat );
54
55	pg = ch_calloc( 1, sizeof( pg_t ) );
56
57	pg->pg_style = style;
58
59	switch ( pg->pg_style ) {
60	case ACL_STYLE_BASE:
61		rc = dnNormalize( 0, NULL, NULL, &pat, &pg->pg_pat, NULL );
62		if ( rc != LDAP_SUCCESS ) {
63			fprintf( stderr, "%s line %d: posixGroup ACL: "
64				"unable to normalize DN \"%s\".\n",
65				fname, lineno, pattern );
66			goto cleanup;
67		}
68		break;
69
70	case ACL_STYLE_EXPAND:
71		ber_dupbv( &pg->pg_pat, &pat );
72		break;
73
74	default:
75		fprintf( stderr, "%s line %d: posixGroup ACL: "
76			"unsupported style \"%s\".\n",
77			fname, lineno, style_strings[ pg->pg_style ] );
78		goto cleanup;
79	}
80
81	/* TODO: use opts to allow the use of different
82	 * group objects and member attributes */
83	if ( pg_posixGroup == NULL ) {
84		pg_posixGroup = oc_find( "posixGroup" );
85		if ( pg_posixGroup == NULL ) {
86			fprintf( stderr, "%s line %d: posixGroup ACL: "
87				"unable to lookup \"posixGroup\" "
88				"objectClass.\n",
89				fname, lineno );
90			goto cleanup;
91		}
92
93		pg_posixAccount = oc_find( "posixAccount" );
94		if ( pg_posixGroup == NULL ) {
95			fprintf( stderr, "%s line %d: posixGroup ACL: "
96				"unable to lookup \"posixAccount\" "
97				"objectClass.\n",
98				fname, lineno );
99			goto cleanup;
100		}
101
102		rc = slap_str2ad( "memberUid", &pg_memberUid, &text );
103		if ( rc != LDAP_SUCCESS ) {
104			fprintf( stderr, "%s line %d: posixGroup ACL: "
105				"unable to lookup \"memberUid\" "
106				"attributeDescription (%d: %s).\n",
107				fname, lineno, rc, text );
108			goto cleanup;
109		}
110
111		rc = slap_str2ad( "uidNumber", &pg_uidNumber, &text );
112		if ( rc != LDAP_SUCCESS ) {
113			fprintf( stderr, "%s line %d: posixGroup ACL: "
114				"unable to lookup \"uidNumber\" "
115				"attributeDescription (%d: %s).\n",
116				fname, lineno, rc, text );
117			goto cleanup;
118		}
119	}
120
121	*privp = (void *)pg;
122	return 0;
123
124cleanup:
125	(void)pg_dynacl_destroy( (void *)pg );
126
127	return 1;
128}
129
130static int
131pg_dynacl_unparse(
132	void		*priv,
133	struct berval	*bv )
134{
135	pg_t		*pg = (pg_t *)priv;
136	char		*ptr;
137
138	bv->bv_len = STRLENOF( " dynacl/posixGroup.expand=" ) + pg->pg_pat.bv_len;
139	bv->bv_val = ch_malloc( bv->bv_len + 1 );
140
141	ptr = lutil_strcopy( bv->bv_val, " dynacl/posixGroup" );
142
143	switch ( pg->pg_style ) {
144	case ACL_STYLE_BASE:
145		ptr = lutil_strcopy( ptr, ".exact=" );
146		break;
147
148	case ACL_STYLE_EXPAND:
149		ptr = lutil_strcopy( ptr, ".expand=" );
150		break;
151
152	default:
153		assert( 0 );
154	}
155
156	ptr = lutil_strncopy( ptr, pg->pg_pat.bv_val, pg->pg_pat.bv_len );
157	ptr[ 0 ] = '\0';
158
159	bv->bv_len = ptr - bv->bv_val;
160
161	return 0;
162}
163
164static int
165pg_dynacl_mask(
166	void			*priv,
167	Operation		*op,
168	Entry			*target,
169	AttributeDescription	*desc,
170	struct berval		*val,
171	int			nmatch,
172	regmatch_t		*matches,
173	slap_access_t		*grant,
174	slap_access_t		*deny )
175{
176	pg_t		*pg = (pg_t *)priv;
177	Entry		*group = NULL,
178			*user = NULL;
179	int		rc;
180	Backend		*be = op->o_bd,
181			*group_be = NULL,
182			*user_be = NULL;
183	struct berval	group_ndn;
184
185	ACL_INVALIDATE( *deny );
186
187	/* get user */
188	if ( target && dn_match( &target->e_nname, &op->o_ndn ) ) {
189		user = target;
190		rc = LDAP_SUCCESS;
191
192	} else {
193		user_be = op->o_bd = select_backend( &op->o_ndn, 0 );
194		if ( op->o_bd == NULL ) {
195			op->o_bd = be;
196			return 0;
197		}
198		rc = be_entry_get_rw( op, &op->o_ndn, pg_posixAccount, pg_uidNumber, 0, &user );
199	}
200
201	if ( rc != LDAP_SUCCESS || user == NULL ) {
202		op->o_bd = be;
203		return 0;
204	}
205
206	/* get target */
207	if ( pg->pg_style == ACL_STYLE_EXPAND ) {
208		char		buf[ 1024 ];
209		struct berval	bv;
210		AclRegexMatches amatches = { 0 };
211
212		amatches.dn_count = nmatch;
213		AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
214
215		bv.bv_len = sizeof( buf ) - 1;
216		bv.bv_val = buf;
217
218		if ( acl_string_expand( &bv, &pg->pg_pat,
219				&target->e_nname,
220				NULL, &amatches ) )
221		{
222			goto cleanup;
223		}
224
225		if ( dnNormalize( 0, NULL, NULL, &bv, &group_ndn,
226				op->o_tmpmemctx ) != LDAP_SUCCESS )
227		{
228			/* did not expand to a valid dn */
229			goto cleanup;
230		}
231
232	} else {
233		group_ndn = pg->pg_pat;
234	}
235
236	if ( target && dn_match( &target->e_nname, &group_ndn ) ) {
237		group = target;
238		rc = LDAP_SUCCESS;
239
240	} else {
241		group_be = op->o_bd = select_backend( &group_ndn, 0 );
242		if ( op->o_bd == NULL ) {
243			goto cleanup;
244		}
245		rc = be_entry_get_rw( op, &group_ndn, pg_posixGroup, pg_memberUid, 0, &group );
246	}
247
248	if ( group_ndn.bv_val != pg->pg_pat.bv_val ) {
249		op->o_tmpfree( group_ndn.bv_val, op->o_tmpmemctx );
250	}
251
252	if ( rc == LDAP_SUCCESS && group != NULL ) {
253		Attribute	*a_uid,
254				*a_member;
255
256		a_uid = attr_find( user->e_attrs, pg_uidNumber );
257		if ( !a_uid || !BER_BVISNULL( &a_uid->a_nvals[ 1 ] ) ) {
258			rc = LDAP_NO_SUCH_ATTRIBUTE;
259
260		} else {
261			a_member = attr_find( group->e_attrs, pg_memberUid );
262			if ( !a_member ) {
263				rc = LDAP_NO_SUCH_ATTRIBUTE;
264
265			} else {
266				rc = value_find_ex( pg_memberUid,
267					SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
268					SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
269					a_member->a_nvals, &a_uid->a_nvals[ 0 ],
270					op->o_tmpmemctx );
271			}
272		}
273
274	} else {
275		rc = LDAP_NO_SUCH_OBJECT;
276	}
277
278
279	if ( rc == LDAP_SUCCESS ) {
280		ACL_LVL_ASSIGN_WRITE( *grant );
281	}
282
283cleanup:;
284	if ( group != NULL && group != target ) {
285		op->o_bd = group_be;
286		be_entry_release_r( op, group );
287		op->o_bd = be;
288	}
289
290	if ( user != NULL && user != target ) {
291		op->o_bd = user_be;
292		be_entry_release_r( op, user );
293		op->o_bd = be;
294	}
295
296	return 0;
297}
298
299static int
300pg_dynacl_destroy(
301	void		*priv )
302{
303	pg_t		*pg = (pg_t *)priv;
304
305	if ( pg != NULL ) {
306		if ( !BER_BVISNULL( &pg->pg_pat ) ) {
307			ber_memfree( pg->pg_pat.bv_val );
308		}
309		ch_free( pg );
310	}
311
312	return 0;
313}
314
315static struct slap_dynacl_t pg_dynacl = {
316	"posixGroup",
317	pg_dynacl_parse,
318	pg_dynacl_unparse,
319	pg_dynacl_mask,
320	pg_dynacl_destroy
321};
322
323int
324init_module( int argc, char *argv[] )
325{
326	return slap_dynacl_register( &pg_dynacl );
327}
328
329#endif /* SLAP_DYNACL */
330