1 /* $NetBSD: passwd.c,v 1.1.1.3 2010/12/12 15:19:10 adam Exp $ */ 2 3 /* passwd.c - password lookup routines */ 4 /* OpenLDAP: pkg/ldap/contrib/slapd-modules/nssov/passwd.c,v 1.1.2.7 2010/04/15 21:32:56 quanah Exp */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2008-2010 The OpenLDAP Foundation. 8 * Portions Copyright 2008 by Howard Chu, Symas Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19 /* ACKNOWLEDGEMENTS: 20 * This code references portions of the nss-ldapd package 21 * written by Arthur de Jong. The nss-ldapd code was forked 22 * from the nss-ldap library written by Luke Howard. 23 */ 24 25 #include "nssov.h" 26 27 /* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY 28 * DESC 'Abstraction of an account with POSIX attributes' 29 * MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) 30 * MAY ( userPassword $ loginShell $ gecos $ description ) ) 31 */ 32 33 /* the basic search filter for searches */ 34 static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)"); 35 36 /* the attributes used in searches */ 37 static struct berval passwd_keys[] = { 38 BER_BVC("uid"), 39 BER_BVC("userPassword"), 40 BER_BVC("uidNumber"), 41 BER_BVC("gidNumber"), 42 BER_BVC("gecos"), 43 BER_BVC("cn"), 44 BER_BVC("homeDirectory"), 45 BER_BVC("loginShell"), 46 BER_BVC("objectClass"), 47 BER_BVNULL 48 }; 49 50 #define UID_KEY 0 51 #define PWD_KEY 1 52 #define UIDN_KEY 2 53 #define GIDN_KEY 3 54 #define GEC_KEY 4 55 #define CN_KEY 5 56 #define DIR_KEY 6 57 #define SHL_KEY 7 58 59 /* default values for attributes */ 60 static struct berval default_passwd_userPassword = BER_BVC("*"); /* unmatchable */ 61 static struct berval default_passwd_homeDirectory = BER_BVC(""); 62 static struct berval default_passwd_loginShell = BER_BVC(""); 63 64 static struct berval shadow_passwd = BER_BVC("x"); 65 66 NSSOV_INIT(passwd) 67 68 /* 69 Checks to see if the specified name is a valid user name. 70 71 This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name 72 and 3.276 Portable Filename Character Set): 73 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426 74 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276 75 76 The standard defines user names valid if they contain characters from 77 the set [A-Za-z0-9._-] where the hyphen should not be used as first 78 character. As an extension this test allows the dolar '$' sign as the last 79 character to support Samba special accounts. 80 */ 81 int isvalidusername(struct berval *bv) 82 { 83 int i; 84 char *name = bv->bv_val; 85 if ((name==NULL)||(name[0]=='\0')) 86 return 0; 87 /* check first character */ 88 if ( ! ( (name[0]>='A' && name[0] <= 'Z') || 89 (name[0]>='a' && name[0] <= 'z') || 90 (name[0]>='0' && name[0] <= '9') || 91 name[0]=='.' || name[0]=='_' ) ) 92 return 0; 93 /* check other characters */ 94 for (i=1;i<bv->bv_len;i++) 95 { 96 if ( name[i]=='$' ) 97 { 98 /* if the char is $ we require it to be the last char */ 99 if (name[i+1]!='\0') 100 return 0; 101 } 102 else if ( ! ( (name[i]>='A' && name[i] <= 'Z') || 103 (name[i]>='a' && name[i] <= 'z') || 104 (name[i]>='0' && name[i] <= '9') || 105 name[i]=='.' || name[i]=='_' || name[i]=='-') ) 106 return 0; 107 } 108 /* no test failed so it must be good */ 109 return -1; 110 } 111 112 /* return 1 on success */ 113 int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid) 114 { 115 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd]; 116 AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc; 117 Entry *e; 118 119 /* check for empty string */ 120 if (!dn->bv_len) 121 return 0; 122 /* try to look up uid within DN string */ 123 if (!strncmp(dn->bv_val,ad->ad_cname.bv_val,ad->ad_cname.bv_len) && 124 dn->bv_val[ad->ad_cname.bv_len] == '=') 125 { 126 struct berval bv, rdn; 127 dnRdn(dn, &rdn); 128 /* check if it is valid */ 129 bv.bv_val = dn->bv_val + ad->ad_cname.bv_len + 1; 130 bv.bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1; 131 if (!isvalidusername(&bv)) 132 return 0; 133 ber_dupbv_x( uid, &bv, op->o_tmpmemctx ); 134 return 1; 135 } 136 /* look up the uid from the entry itself */ 137 if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS) 138 { 139 Attribute *a = attr_find(e->e_attrs, ad); 140 if (a) { 141 ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx); 142 } 143 be_entry_release_r(op, e); 144 if (a) 145 return 1; 146 } 147 return 0; 148 } 149 150 int nssov_name2dn_cb(Operation *op,SlapReply *rs) 151 { 152 if ( rs->sr_type == REP_SEARCH ) 153 { 154 struct berval *bv = op->o_callback->sc_private; 155 if ( !BER_BVISNULL(bv)) { 156 op->o_tmpfree( bv->bv_val, op->o_tmpmemctx ); 157 BER_BVZERO(bv); 158 return LDAP_ALREADY_EXISTS; 159 } 160 ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx); 161 } 162 return LDAP_SUCCESS; 163 } 164 165 int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn) 166 { 167 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd]; 168 char fbuf[1024]; 169 struct berval filter = {sizeof(fbuf),fbuf}; 170 slap_callback cb = {0}; 171 SlapReply rs = {REP_RESULT}; 172 Operation op2; 173 int rc; 174 175 /* if it isn't a valid username, just bail out now */ 176 if (!isvalidusername(uid)) 177 return 0; 178 /* we have to look up the entry */ 179 nssov_filter_byid(mi,UID_KEY,uid,&filter); 180 BER_BVZERO(dn); 181 cb.sc_private = dn; 182 cb.sc_response = nssov_name2dn_cb; 183 op2 = *op; 184 op2.o_callback = &cb; 185 op2.o_req_dn = mi->mi_base; 186 op2.o_req_ndn = mi->mi_base; 187 op2.ors_scope = mi->mi_scope; 188 op2.ors_filterstr = filter; 189 op2.ors_filter = str2filter_x( op, filter.bv_val ); 190 op2.ors_attrs = slap_anlist_no_attrs; 191 op2.ors_tlimit = SLAP_NO_LIMIT; 192 op2.ors_slimit = SLAP_NO_LIMIT; 193 rc = op2.o_bd->be_search( &op2, &rs ); 194 filter_free_x( op, op2.ors_filter, 1 ); 195 return rc == LDAP_SUCCESS && !BER_BVISNULL(dn); 196 } 197 198 /* the maximum number of uidNumber attributes per entry */ 199 #define MAXUIDS_PER_ENTRY 5 200 201 NSSOV_CBPRIV(passwd, 202 char buf[256]; 203 struct berval name; 204 struct berval id;); 205 206 static struct berval shadowclass = BER_BVC("shadowAccount"); 207 208 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry) 209 { 210 int32_t tmpint32; 211 struct berval tmparr[2], tmpuid[2]; 212 const char **tmpvalues; 213 char *tmp; 214 struct berval *names; 215 struct berval *uids; 216 struct berval passwd = {0}; 217 gid_t gid; 218 struct berval gecos; 219 struct berval homedir; 220 struct berval shell; 221 Attribute *a; 222 int i,j; 223 int use_shadow = 0; 224 /* get the usernames for this entry */ 225 if (BER_BVISNULL(&cbp->name)) 226 { 227 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc); 228 if (!a) 229 { 230 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 231 entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0); 232 return 0; 233 } 234 names = a->a_vals; 235 } 236 else 237 { 238 names=tmparr; 239 names[0]=cbp->name; 240 BER_BVZERO(&names[1]); 241 } 242 /* get the password for this entry */ 243 a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass); 244 if ( a ) { 245 for ( i=0; i<a->a_numvals; i++) { 246 if ( bvmatch( &shadowclass, &a->a_nvals[i] )) { 247 use_shadow = 1; 248 break; 249 } 250 } 251 } 252 if ( use_shadow ) 253 { 254 /* if the entry has a shadowAccount entry, point to that instead */ 255 passwd = shadow_passwd; 256 } 257 else 258 { 259 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc); 260 if (a) 261 get_userpassword(&a->a_vals[0], &passwd); 262 if (BER_BVISNULL(&passwd)) 263 passwd=default_passwd_userPassword; 264 } 265 /* get the uids for this entry */ 266 if (BER_BVISNULL(&cbp->id)) 267 { 268 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc); 269 if ( !a ) 270 { 271 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 272 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,0); 273 return 0; 274 } 275 uids = a->a_vals; 276 } 277 else 278 { 279 uids = tmpuid; 280 uids[0] = cbp->id; 281 BER_BVZERO(&uids[1]); 282 } 283 /* get the gid for this entry */ 284 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc); 285 if (!a) 286 { 287 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 288 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0); 289 return 0; 290 } 291 else if (a->a_numvals != 1) 292 { 293 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n", 294 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0); 295 } 296 gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0); 297 if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0')) 298 { 299 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n", 300 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0); 301 return 0; 302 } 303 /* get the gecos for this entry (fall back to cn) */ 304 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc); 305 if (!a) 306 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc); 307 if (!a || !a->a_numvals) 308 { 309 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n", 310 entry->e_name.bv_val, 311 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val, 312 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val); 313 return 0; 314 } 315 else if (a->a_numvals > 1) 316 { 317 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n", 318 entry->e_name.bv_val, 319 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val, 320 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val); 321 } 322 gecos=a->a_vals[0]; 323 /* get the home directory for this entry */ 324 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc); 325 if (!a) 326 { 327 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 328 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0); 329 homedir=default_passwd_homeDirectory; 330 } 331 else 332 { 333 if (a->a_numvals > 1) 334 { 335 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n", 336 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0); 337 } 338 homedir=a->a_vals[0]; 339 if (homedir.bv_val[0]=='\0') 340 homedir=default_passwd_homeDirectory; 341 } 342 /* get the shell for this entry */ 343 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc); 344 if (!a) 345 { 346 shell=default_passwd_loginShell; 347 } 348 else 349 { 350 if (a->a_numvals > 1) 351 { 352 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n", 353 entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val,0); 354 } 355 shell=a->a_vals[0]; 356 if (shell.bv_val[0]=='\0') 357 shell=default_passwd_loginShell; 358 } 359 /* write the entries */ 360 for (i=0;!BER_BVISNULL(&names[i]);i++) 361 { 362 if (!isvalidusername(&names[i])) 363 { 364 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n", 365 entry->e_name.bv_val,names[i].bv_val,0); 366 } 367 else 368 { 369 for (j=0;!BER_BVISNULL(&uids[j]);j++) 370 { 371 char *tmp; 372 uid_t uid; 373 uid = strtol(uids[j].bv_val, &tmp, 0); 374 if ( *tmp ) { 375 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n", 376 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val, 377 names[i].bv_val); 378 continue; 379 } 380 WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); 381 WRITE_BERVAL(cbp->fp,&names[i]); 382 WRITE_BERVAL(cbp->fp,&passwd); 383 WRITE_TYPE(cbp->fp,uid,uid_t); 384 WRITE_TYPE(cbp->fp,gid,gid_t); 385 WRITE_BERVAL(cbp->fp,&gecos); 386 WRITE_BERVAL(cbp->fp,&homedir); 387 WRITE_BERVAL(cbp->fp,&shell); 388 } 389 } 390 } 391 return 0; 392 } 393 394 NSSOV_CB(passwd) 395 396 NSSOV_HANDLE( 397 passwd,byname, 398 char fbuf[1024]; 399 struct berval filter = {sizeof(fbuf)}; 400 filter.bv_val = fbuf; 401 READ_STRING(fp,cbp.buf); 402 cbp.name.bv_len = tmpint32; 403 cbp.name.bv_val = cbp.buf; 404 if (!isvalidusername(&cbp.name)) { 405 Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name\n",cbp.name.bv_val,0,0); 406 return -1; 407 } 408 BER_BVZERO(&cbp.id); , 409 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val,0,0);, 410 NSLCD_ACTION_PASSWD_BYNAME, 411 nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter) 412 ) 413 414 NSSOV_HANDLE( 415 passwd,byuid, 416 uid_t uid; 417 char fbuf[1024]; 418 struct berval filter = {sizeof(fbuf)}; 419 filter.bv_val = fbuf; 420 READ_TYPE(fp,uid,uid_t); 421 cbp.id.bv_val = cbp.buf; 422 cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid); 423 BER_BVZERO(&cbp.name);, 424 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)\n",cbp.id.bv_val,0,0);, 425 NSLCD_ACTION_PASSWD_BYUID, 426 nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter) 427 ) 428 429 NSSOV_HANDLE( 430 passwd,all, 431 struct berval filter; 432 /* no parameters to read */ 433 BER_BVZERO(&cbp.name); 434 BER_BVZERO(&cbp.id);, 435 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n",0,0,0);, 436 NSLCD_ACTION_PASSWD_ALL, 437 (filter=cbp.mi->mi_filter,0) 438 ) 439