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