1/*	$NetBSD: slapacl.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 2004-2021 The OpenLDAP Foundation.
7 * Portions Copyright 2004 Pierangelo Masarati.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18/* ACKNOWLEDGEMENTS:
19 * This work was initially developed by Pierangelo Masarati for inclusion
20 * in OpenLDAP Software.
21 */
22
23#include <sys/cdefs.h>
24__RCSID("$NetBSD: slapacl.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
25
26#include "portable.h"
27
28#include <stdio.h>
29
30#include <ac/stdlib.h>
31
32#include <ac/ctype.h>
33#include <ac/string.h>
34#include <ac/socket.h>
35#include <ac/unistd.h>
36
37#include <lber.h>
38#include <ldif.h>
39#include <lutil.h>
40
41#include "slapcommon.h"
42
43static int
44print_access(
45	Operation		*op,
46	Entry			*e,
47	AttributeDescription	*desc,
48	struct berval		*val,
49	struct berval		*nval )
50{
51	int			rc;
52	slap_mask_t		mask;
53	char			accessmaskbuf[ACCESSMASK_MAXLEN];
54
55	rc = access_allowed_mask( op, e, desc, nval, ACL_AUTH, NULL, &mask );
56
57	fprintf( stderr, "%s%s%s: %s\n",
58			desc->ad_cname.bv_val,
59			( val && !BER_BVISNULL( val ) ) ? "=" : "",
60			( val && !BER_BVISNULL( val ) ) ?
61				( desc == slap_schema.si_ad_userPassword ?
62					"****" : val->bv_val ) : "",
63			accessmask2str( mask, accessmaskbuf, 1 ) );
64
65	return rc;
66}
67
68int
69slapacl( int argc, char **argv )
70{
71	int			rc = EXIT_SUCCESS;
72	const char		*progname = "slapacl";
73	Connection		conn = { 0 };
74	Listener		listener;
75	OperationBuffer	opbuf;
76	Operation		*op = NULL;
77	Entry			e = { 0 }, *ep = &e;
78	char			*attr = NULL;
79	int			doclose = 0;
80	BackendDB		*bd;
81	void			*thrctx;
82
83	slap_tool_init( progname, SLAPACL, argc, argv );
84
85	if ( !dryrun ) {
86		int	i = 0;
87
88		LDAP_STAILQ_FOREACH( bd, &backendDB, be_next ) {
89			if ( bd != be && backend_startup( bd ) ) {
90				fprintf( stderr, "backend_startup(#%d%s%s) failed\n",
91						i,
92						bd->be_suffix ? ": " : "",
93						bd->be_suffix ? bd->be_suffix[0].bv_val : "" );
94				rc = 1;
95				goto destroy;
96			}
97
98			i++;
99		}
100	}
101
102	argv = &argv[ optind ];
103	argc -= optind;
104
105	thrctx = ldap_pvt_thread_pool_context();
106	connection_fake_init( &conn, &opbuf, thrctx );
107	op = &opbuf.ob_op;
108	op->o_tmpmemctx = NULL;
109
110	conn.c_listener = &listener;
111	conn.c_listener_url = listener_url;
112	conn.c_peer_domain = peer_domain;
113	conn.c_peer_name = peer_name;
114	conn.c_sock_name = sock_name;
115	op->o_ssf = ssf;
116	op->o_transport_ssf = transport_ssf;
117	op->o_tls_ssf = tls_ssf;
118	op->o_sasl_ssf = sasl_ssf;
119
120	if ( !BER_BVISNULL( &authcID ) ) {
121		if ( !BER_BVISNULL( &authcDN ) ) {
122			fprintf( stderr, "both authcID=\"%s\" "
123					"and authcDN=\"%s\" provided\n",
124					authcID.bv_val, authcDN.bv_val );
125			rc = 1;
126			goto destroy;
127		}
128
129		rc = slap_sasl_getdn( &conn, op, &authcID, NULL,
130				&authcDN, SLAP_GETDN_AUTHCID );
131		if ( rc != LDAP_SUCCESS ) {
132			fprintf( stderr, "authcID: <%s> check failed %d (%s)\n",
133					authcID.bv_val, rc,
134					ldap_err2string( rc ) );
135			rc = 1;
136			goto destroy;
137		}
138
139	} else if ( !BER_BVISNULL( &authcDN ) ) {
140		struct berval	ndn;
141
142		rc = dnNormalize( 0, NULL, NULL, &authcDN, &ndn, NULL );
143		if ( rc != LDAP_SUCCESS ) {
144			fprintf( stderr, "autchDN=\"%s\" normalization failed %d (%s)\n",
145					authcDN.bv_val, rc,
146					ldap_err2string( rc ) );
147			rc = 1;
148			goto destroy;
149		}
150		ch_free( authcDN.bv_val );
151		authcDN = ndn;
152	}
153
154	if ( !BER_BVISNULL( &authzID ) ) {
155		if ( !BER_BVISNULL( &authzDN ) ) {
156			fprintf( stderr, "both authzID=\"%s\" "
157					"and authzDN=\"%s\" provided\n",
158					authzID.bv_val, authzDN.bv_val );
159			rc = 1;
160			goto destroy;
161		}
162
163		rc = slap_sasl_getdn( &conn, op, &authzID, NULL,
164				&authzDN, SLAP_GETDN_AUTHZID );
165		if ( rc != LDAP_SUCCESS ) {
166			fprintf( stderr, "authzID: <%s> check failed %d (%s)\n",
167					authzID.bv_val, rc,
168					ldap_err2string( rc ) );
169			rc = 1;
170			goto destroy;
171		}
172
173	} else if ( !BER_BVISNULL( &authzDN ) ) {
174		struct berval	ndn;
175
176		rc = dnNormalize( 0, NULL, NULL, &authzDN, &ndn, NULL );
177		if ( rc != LDAP_SUCCESS ) {
178			fprintf( stderr, "autchDN=\"%s\" normalization failed %d (%s)\n",
179					authzDN.bv_val, rc,
180					ldap_err2string( rc ) );
181			rc = 1;
182			goto destroy;
183		}
184		ch_free( authzDN.bv_val );
185		authzDN = ndn;
186	}
187
188
189	if ( !BER_BVISNULL( &authcDN ) ) {
190		fprintf( stderr, "authcDN: \"%s\"\n", authcDN.bv_val );
191	}
192
193	if ( !BER_BVISNULL( &authzDN ) ) {
194		fprintf( stderr, "authzDN: \"%s\"\n", authzDN.bv_val );
195	}
196
197	if ( !BER_BVISNULL( &authzDN ) ) {
198		op->o_dn = authzDN;
199		op->o_ndn = authzDN;
200
201		if ( !BER_BVISNULL( &authcDN ) ) {
202			op->o_conn->c_dn = authcDN;
203			op->o_conn->c_ndn = authcDN;
204
205		} else {
206			op->o_conn->c_dn = authzDN;
207			op->o_conn->c_ndn = authzDN;
208		}
209
210	} else if ( !BER_BVISNULL( &authcDN ) ) {
211		op->o_conn->c_dn = authcDN;
212		op->o_conn->c_ndn = authcDN;
213		op->o_dn = authcDN;
214		op->o_ndn = authcDN;
215	}
216
217	assert( !BER_BVISNULL( &baseDN ) );
218	rc = dnPrettyNormal( NULL, &baseDN, &e.e_name, &e.e_nname, NULL );
219	if ( rc != LDAP_SUCCESS ) {
220		fprintf( stderr, "base=\"%s\" normalization failed %d (%s)\n",
221				baseDN.bv_val, rc,
222				ldap_err2string( rc ) );
223		rc = 1;
224		goto destroy;
225	}
226
227	op->o_bd = be;
228	if ( op->o_bd == NULL ) {
229		/* NOTE: if no database could be found (e.g. because
230		 * accessing the rootDSE or so), use the frontendDB
231		 * rules; might need work */
232		op->o_bd = frontendDB;
233	}
234
235	if ( !dryrun ) {
236		ID	id;
237
238		if ( be == NULL ) {
239			fprintf( stderr, "%s: no target database "
240				"has been found for baseDN=\"%s\"; "
241				"you may try with \"-u\" (dry run).\n",
242				baseDN.bv_val, progname );
243			rc = 1;
244			goto destroy;
245		}
246
247		if ( !be->be_entry_open ||
248			!be->be_entry_close ||
249			!be->be_dn2id_get ||
250			!be->be_entry_get )
251		{
252			fprintf( stderr, "%s: target database "
253				"doesn't support necessary operations; "
254				"you may try with \"-u\" (dry run).\n",
255				progname );
256			rc = 1;
257			goto destroy;
258		}
259
260		if ( be->be_entry_open( be, 0 ) != 0 ) {
261			fprintf( stderr, "%s: could not open database.\n",
262				progname );
263			rc = 1;
264			goto destroy;
265		}
266
267		doclose = 1;
268
269		id = be->be_dn2id_get( be, &e.e_nname );
270		if ( id == NOID ) {
271			fprintf( stderr, "%s: unable to fetch ID of DN \"%s\"\n",
272				progname, e.e_nname.bv_val );
273			rc = 1;
274			goto destroy;
275		}
276		ep = be->be_entry_get( be, id );
277		if ( ep == NULL ) {
278			fprintf( stderr, "%s: unable to fetch entry \"%s\" (%lu)\n",
279				progname, e.e_nname.bv_val, id );
280			rc = 1;
281			goto destroy;
282
283		}
284
285		if ( argc == 0 ) {
286			Attribute	*a;
287
288			(void)print_access( op, ep, slap_schema.si_ad_entry, NULL, NULL );
289			(void)print_access( op, ep, slap_schema.si_ad_children, NULL, NULL );
290
291			for ( a = ep->e_attrs; a; a = a->a_next ) {
292				int	i;
293
294				for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
295					(void)print_access( op, ep, a->a_desc,
296							&a->a_vals[ i ],
297							&a->a_nvals[ i ] );
298				}
299			}
300		}
301	}
302
303	for ( ; argc--; argv++ ) {
304		slap_mask_t		mask;
305		AttributeDescription	*desc = NULL;
306		struct berval		val = BER_BVNULL,
307					*valp = NULL;
308		const char		*text;
309		char			accessmaskbuf[ACCESSMASK_MAXLEN];
310		char			*accessstr;
311		slap_access_t		access = ACL_AUTH;
312
313		if ( attr == NULL ) {
314			attr = argv[ 0 ];
315		}
316
317		val.bv_val = strchr( attr, ':' );
318		if ( val.bv_val != NULL ) {
319			val.bv_val[0] = '\0';
320			val.bv_val++;
321			val.bv_len = strlen( val.bv_val );
322			valp = &val;
323		}
324
325		accessstr = strchr( attr, '/' );
326		if ( accessstr != NULL ) {
327			int	invalid = 0;
328
329			accessstr[0] = '\0';
330			accessstr++;
331			access = str2access( accessstr );
332			switch ( access ) {
333			case ACL_INVALID_ACCESS:
334				fprintf( stderr, "unknown access \"%s\" for attribute \"%s\"\n",
335						accessstr, attr );
336				invalid = 1;
337				break;
338
339			case ACL_NONE:
340				fprintf( stderr, "\"none\" not allowed for attribute \"%s\"\n",
341						attr );
342				invalid = 1;
343				break;
344
345			default:
346				break;
347			}
348
349			if ( invalid ) {
350				if ( continuemode ) {
351					continue;
352				}
353				break;
354			}
355		}
356
357		rc = slap_str2ad( attr, &desc, &text );
358		if ( rc != LDAP_SUCCESS ) {
359			fprintf( stderr, "slap_str2ad(%s) failed %d (%s)\n",
360					attr, rc, ldap_err2string( rc ) );
361			if ( continuemode ) {
362				continue;
363			}
364			break;
365		}
366
367		rc = access_allowed_mask( op, ep, desc, valp, access,
368				NULL, &mask );
369
370		if ( accessstr ) {
371			fprintf( stderr, "%s access to %s%s%s: %s\n",
372					accessstr,
373					desc->ad_cname.bv_val,
374					val.bv_val ? "=" : "",
375					val.bv_val ? val.bv_val : "",
376					rc ? "ALLOWED" : "DENIED" );
377
378		} else {
379			fprintf( stderr, "%s%s%s: %s\n",
380					desc->ad_cname.bv_val,
381					val.bv_val ? "=" : "",
382					val.bv_val ? val.bv_val : "",
383					accessmask2str( mask, accessmaskbuf, 1 ) );
384		}
385		rc = 0;
386		attr = NULL;
387	}
388
389destroy:;
390	if ( !BER_BVISNULL( &e.e_name ) ) {
391		ber_memfree( e.e_name.bv_val );
392	}
393	if ( !BER_BVISNULL( &e.e_nname ) ) {
394		ber_memfree( e.e_nname.bv_val );
395	}
396	if ( !dryrun && be ) {
397		if ( ep && ep != &e ) {
398			be_entry_release_r( op, ep );
399		}
400		if ( doclose ) {
401			be->be_entry_close( be );
402		}
403
404		LDAP_STAILQ_FOREACH( bd, &backendDB, be_next ) {
405			if ( bd != be ) {
406				backend_shutdown( bd );
407			}
408		}
409	}
410
411	if ( slap_tool_destroy())
412		rc = EXIT_FAILURE;
413
414	return rc;
415}
416
417