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