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