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