xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-passwd/search.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: search.c,v 1.1.1.4 2014/05/28 09:58:51 tron Exp $	*/
2 
3 /* search.c - /etc/passwd backend search function */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 1998-2014 The OpenLDAP Foundation.
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 the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
19  * All rights reserved.
20  *
21  * Redistribution and use in source and binary forms are permitted
22  * provided that this notice is preserved and that due credit is given
23  * to the University of Michigan at Ann Arbor. The name of the University
24  * may not be used to endorse or promote products derived from this
25  * software without specific prior written permission. This software
26  * is provided ``as is'' without express or implied warranty.
27  */
28 /* ACKNOWLEDGEMENTS:
29  * This work was originally developed by the University of Michigan
30  * (as part of U-MICH LDAP).  Additional significant contributors
31  * include:
32  *     Hallvard B. Furuseth
33  *     Howard Chu
34  *     Kurt D. Zeilenga
35  */
36 
37 #include "portable.h"
38 
39 #include <stdio.h>
40 
41 #include <ac/ctype.h>
42 #include <ac/socket.h>
43 #include <ac/string.h>
44 #include <ac/time.h>
45 
46 #include <pwd.h>
47 
48 #include "slap.h"
49 #include "back-passwd.h"
50 
51 static void pw_start( Backend *be );
52 
53 static int pw2entry(
54 	Backend		*be,
55 	struct passwd	*pw,
56 	Entry		*ep );
57 
58 int
59 passwd_back_search(
60     Operation	*op,
61     SlapReply	*rs )
62 {
63 	struct passwd	*pw;
64 	time_t		stoptime = (time_t)-1;
65 
66 	LDAPRDN rdn = NULL;
67 	struct berval parent = BER_BVNULL;
68 
69 	AttributeDescription *ad_objectClass = slap_schema.si_ad_objectClass;
70 
71 	if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
72 		stoptime = op->o_time + op->ors_tlimit;
73 	}
74 
75 	/* Handle a query for the base of this backend */
76 	if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
77 		struct berval	val;
78 
79 		rs->sr_matched = op->o_req_dn.bv_val;
80 
81 		if( op->ors_scope != LDAP_SCOPE_ONELEVEL ) {
82 			AttributeDescription	*desc = NULL;
83 			char			*next;
84 			Entry			e = { 0 };
85 
86 			/* Create an entry corresponding to the base DN */
87 			e.e_name.bv_val = ch_strdup( op->o_req_dn.bv_val );
88 			e.e_name.bv_len = op->o_req_dn.bv_len;
89 			e.e_nname.bv_val =  ch_strdup( op->o_req_ndn.bv_val );
90 			e.e_nname.bv_len = op->o_req_ndn.bv_len;
91 
92 			/* Use the first attribute of the DN
93 		 	* as an attribute within the entry itself.
94 		 	*/
95 			if( ldap_bv2rdn( &op->o_req_dn, &rdn, &next,
96 				LDAP_DN_FORMAT_LDAP ) )
97 			{
98 				rs->sr_err = LDAP_INVALID_DN_SYNTAX;
99 				goto done;
100 			}
101 
102 			if( slap_bv2ad( &rdn[0]->la_attr, &desc, &rs->sr_text )) {
103 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
104 				ldap_rdnfree(rdn);
105 				goto done;
106 			}
107 
108 			attr_merge_normalize_one( &e, desc, &rdn[0]->la_value, NULL );
109 
110 			ldap_rdnfree(rdn);
111 			rdn = NULL;
112 
113 			/* Every entry needs an objectclass. We don't really
114 			 * know if our hardcoded choice here agrees with the
115 			 * DN that was configured for this backend, but it's
116 			 * better than nothing.
117 			 *
118 			 * should be a configuratable item
119 			 */
120 			BER_BVSTR( &val, "organizationalUnit" );
121 			attr_merge_one( &e, ad_objectClass, &val, NULL );
122 
123 			if ( test_filter( op, &e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
124 				rs->sr_entry = &e;
125 				rs->sr_attrs = op->ors_attrs;
126 				rs->sr_flags = REP_ENTRY_MODIFIABLE;
127 				send_search_entry( op, rs );
128 				rs->sr_flags = 0;
129 				rs->sr_attrs = NULL;
130 			}
131 
132 			entry_clean( &e );
133 		}
134 
135 		if ( op->ors_scope != LDAP_SCOPE_BASE ) {
136 			/* check all our "children" */
137 
138 			ldap_pvt_thread_mutex_lock( &passwd_mutex );
139 			pw_start( op->o_bd );
140 			for ( pw = getpwent(); pw != NULL; pw = getpwent() ) {
141 				Entry		e = { 0 };
142 
143 				/* check for abandon */
144 				if ( op->o_abandon ) {
145 					endpwent();
146 					ldap_pvt_thread_mutex_unlock( &passwd_mutex );
147 					return( SLAPD_ABANDON );
148 				}
149 
150 				/* check time limit */
151 				if ( op->ors_tlimit != SLAP_NO_LIMIT
152 						&& slap_get_time() > stoptime )
153 				{
154 					send_ldap_error( op, rs, LDAP_TIMELIMIT_EXCEEDED, NULL );
155 					endpwent();
156 					ldap_pvt_thread_mutex_unlock( &passwd_mutex );
157 					return( 0 );
158 				}
159 
160 				if ( pw2entry( op->o_bd, pw, &e ) ) {
161 					rs->sr_err = LDAP_OTHER;
162 					endpwent();
163 					ldap_pvt_thread_mutex_unlock( &passwd_mutex );
164 					goto done;
165 				}
166 
167 				if ( test_filter( op, &e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
168 					/* check size limit */
169 					if ( --op->ors_slimit == -1 ) {
170 						send_ldap_error( op, rs, LDAP_SIZELIMIT_EXCEEDED, NULL );
171 						endpwent();
172 						ldap_pvt_thread_mutex_unlock( &passwd_mutex );
173 						return( 0 );
174 					}
175 
176 					rs->sr_entry = &e;
177 					rs->sr_attrs = op->ors_attrs;
178 					rs->sr_flags = REP_ENTRY_MODIFIABLE;
179 					send_search_entry( op, rs );
180 					rs->sr_flags = 0;
181 					rs->sr_entry = NULL;
182 				}
183 
184 				entry_clean( &e );
185 			}
186 			endpwent();
187 			ldap_pvt_thread_mutex_unlock( &passwd_mutex );
188 		}
189 
190 	} else {
191 		char	*next;
192 		Entry	e = { 0 };
193 		int	rc;
194 
195 		if (! be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
196 			dnParent( &op->o_req_ndn, &parent );
197 		}
198 
199 		/* This backend is only one layer deep. Don't answer requests for
200 		 * anything deeper than that.
201 		 */
202 		if( !be_issuffix( op->o_bd, &parent ) ) {
203 			int i;
204 			for( i=0; op->o_bd->be_nsuffix[i].bv_val != NULL; i++ ) {
205 				if( dnIsSuffix( &op->o_req_ndn, &op->o_bd->be_nsuffix[i] ) ) {
206 					rs->sr_matched = op->o_bd->be_suffix[i].bv_val;
207 					break;
208 				}
209 			}
210 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
211 			goto done;
212 		}
213 
214 		if( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
215 			goto done;
216 		}
217 
218 		if ( ldap_bv2rdn( &op->o_req_dn, &rdn, &next,
219 			LDAP_DN_FORMAT_LDAP ))
220 		{
221 			rs->sr_err = LDAP_OTHER;
222 			goto done;
223 		}
224 
225 		ldap_pvt_thread_mutex_lock( &passwd_mutex );
226 		pw_start( op->o_bd );
227 		pw = getpwnam( rdn[0]->la_value.bv_val );
228 		if ( pw == NULL ) {
229 			rs->sr_matched = parent.bv_val;
230 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
231 			ldap_pvt_thread_mutex_unlock( &passwd_mutex );
232 			goto done;
233 		}
234 
235 		rc = pw2entry( op->o_bd, pw, &e );
236 		ldap_pvt_thread_mutex_unlock( &passwd_mutex );
237 		if ( rc ) {
238 			rs->sr_err = LDAP_OTHER;
239 			goto done;
240 		}
241 
242 		if ( test_filter( op, &e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
243 			rs->sr_entry = &e;
244 			rs->sr_attrs = op->ors_attrs;
245 			rs->sr_flags = REP_ENTRY_MODIFIABLE;
246 			send_search_entry( op, rs );
247 			rs->sr_flags = 0;
248 			rs->sr_entry = NULL;
249 			rs->sr_attrs = NULL;
250 		}
251 
252 		entry_clean( &e );
253 	}
254 
255 done:
256 	if( rs->sr_err != LDAP_NO_SUCH_OBJECT ) rs->sr_matched = NULL;
257 	send_ldap_result( op, rs );
258 
259 	if( rdn != NULL ) ldap_rdnfree( rdn );
260 
261 	return( 0 );
262 }
263 
264 static void
265 pw_start(
266 	Backend *be
267 )
268 {
269 	endpwent();
270 
271 #ifdef HAVE_SETPWFILE
272 	if ( be->be_private != NULL ) {
273 		(void) setpwfile( (char *) be->be_private );
274 	}
275 #endif /* HAVE_SETPWFILE */
276 }
277 
278 static int
279 pw2entry( Backend *be, struct passwd *pw, Entry *e )
280 {
281 	size_t		pwlen;
282 	struct berval	val;
283 	struct berval	bv;
284 
285 	int		rc;
286 
287 	/*
288 	 * from pw we get pw_name and make it cn
289 	 * give it an objectclass of person.
290 	 */
291 
292 	pwlen = strlen( pw->pw_name );
293 	val.bv_len = STRLENOF("uid=,") + ( pwlen + be->be_suffix[0].bv_len );
294 	val.bv_val = ch_malloc( val.bv_len + 1 );
295 
296 	/* rdn attribute type should be a configuratable item */
297 	sprintf( val.bv_val, "uid=%s,%s",
298 		pw->pw_name, be->be_suffix[0].bv_val );
299 
300 	rc = dnNormalize( 0, NULL, NULL, &val, &bv, NULL );
301 	if( rc != LDAP_SUCCESS ) {
302 		free( val.bv_val );
303 		return( -1 );
304 	}
305 
306 	e->e_name = val;
307 	e->e_nname = bv;
308 
309 	e->e_attrs = NULL;
310 
311 	/* objectclasses should be configurable items */
312 	BER_BVSTR( &val, "person" );
313 	attr_merge_one( e, slap_schema.si_ad_objectClass, &val, NULL );
314 
315 	BER_BVSTR( &val, "uidObject" );
316 	attr_merge_one( e, slap_schema.si_ad_objectClass, &val, NULL );
317 
318 	val.bv_val = pw->pw_name;
319 	val.bv_len = pwlen;
320 	attr_merge_normalize_one( e, slap_schema.si_ad_uid, &val, NULL );	/* required by uidObject */
321 	attr_merge_normalize_one( e, slap_schema.si_ad_cn, &val, NULL );	/* required by person */
322 	attr_merge_normalize_one( e, ad_sn, &val, NULL );	/* required by person */
323 
324 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS
325 	/*
326 	 * if gecos is present, add it as a cn. first process it
327 	 * according to standard BSD usage. If the processed cn has
328 	 * a space, use the tail as the surname.
329 	 */
330 	if (pw->pw_gecos[0]) {
331 		char *s;
332 
333 		ber_str2bv( pw->pw_gecos, 0, 0, &val );
334 		attr_merge_normalize_one( e, ad_desc, &val, NULL );
335 
336 		s = ber_bvchr( &val, ',' );
337 		if ( s ) *s = '\0';
338 
339 		s = ber_bvchr( &val, '&' );
340 		if ( s ) {
341 			char buf[1024];
342 
343 			if( val.bv_len + pwlen < sizeof(buf) ) {
344 				int i = s - val.bv_val;
345 				strncpy( buf, val.bv_val, i );
346 				s = buf + i;
347 				strcpy( s, pw->pw_name );
348 				*s = TOUPPER((unsigned char)*s);
349 				strcat( s, val.bv_val + i + 1 );
350 				val.bv_val = buf;
351 			}
352 		}
353 		val.bv_len = strlen( val.bv_val );
354 
355 		if ( val.bv_len && strcasecmp( val.bv_val, pw->pw_name ) ) {
356 			attr_merge_normalize_one( e, slap_schema.si_ad_cn, &val, NULL );
357 		}
358 
359 		if ( ( s = strrchr(val.bv_val, ' ' ) ) ) {
360 			ber_str2bv( s + 1, 0, 0, &val );
361 			attr_merge_normalize_one( e, ad_sn, &val, NULL );
362 		}
363 	}
364 #endif /* HAVE_STRUCT_PASSWD_PW_GECOS */
365 
366 	return( 0 );
367 }
368