1/* $NetBSD: passwd.c,v 1.3 2021/08/14 16:14:52 christos Exp $ */ 2 3/* passwd.c - password lookup routines */ 4/* $OpenLDAP$ */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2008-2021 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 */ 34static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)"); 35 36/* the attributes used in searches */ 37static 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 */ 60static struct berval default_passwd_userPassword = BER_BVC("*"); /* unmatchable */ 61static struct berval default_passwd_homeDirectory = BER_BVC(""); 62static struct berval default_passwd_loginShell = BER_BVC(""); 63 64static struct berval shadow_passwd = BER_BVC("x"); 65 66NSSOV_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*/ 81int 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 */ 113int 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 150int 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 165int 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 201NSSOV_CBPRIV(passwd, 202 char buf[256]; 203 struct berval name; 204 struct berval id;); 205 206static struct berval shadowclass = BER_BVC("shadowAccount"); 207 208static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry) 209{ 210 int32_t tmpint32; 211 struct berval tmparr[2], tmpuid[2]; 212 char *tmp; 213 struct berval *names; 214 struct berval *uids; 215 struct berval passwd = {0}; 216 gid_t gid; 217 struct berval gecos; 218 struct berval homedir; 219 struct berval shell; 220 Attribute *a; 221 int i,j; 222 int use_shadow = 0; 223 /* get the usernames for this entry */ 224 if (BER_BVISNULL(&cbp->name)) 225 { 226 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc); 227 if (!a) 228 { 229 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 230 entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val ); 231 return 0; 232 } 233 names = a->a_vals; 234 } 235 else 236 { 237 names=tmparr; 238 names[0]=cbp->name; 239 BER_BVZERO(&names[1]); 240 } 241 /* get the password for this entry */ 242 a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass); 243 if ( a ) { 244 for ( i=0; i<a->a_numvals; i++) { 245 if ( bvmatch( &shadowclass, &a->a_nvals[i] )) { 246 use_shadow = 1; 247 break; 248 } 249 } 250 } 251 if ( use_shadow ) 252 { 253 /* if the entry has a shadowAccount entry, point to that instead */ 254 passwd = shadow_passwd; 255 } 256 else 257 { 258 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc); 259 if (a) 260 get_userpassword(&a->a_vals[0], &passwd); 261 if (BER_BVISNULL(&passwd)) 262 passwd=default_passwd_userPassword; 263 } 264 /* get the uids for this entry */ 265 if (BER_BVISNULL(&cbp->id)) 266 { 267 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc); 268 if ( !a ) 269 { 270 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 271 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val ); 272 return 0; 273 } 274 uids = a->a_vals; 275 } 276 else 277 { 278 uids = tmpuid; 279 uids[0] = cbp->id; 280 BER_BVZERO(&uids[1]); 281 } 282 /* get the gid for this entry */ 283 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc); 284 if (!a) 285 { 286 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 287 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val ); 288 return 0; 289 } 290 else if (a->a_numvals != 1) 291 { 292 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n", 293 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val ); 294 } 295 gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0); 296 if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0')) 297 { 298 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n", 299 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val ); 300 return 0; 301 } 302 /* get the gecos for this entry (fall back to cn) */ 303 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc); 304 if (!a) 305 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc); 306 if (!a || !a->a_numvals) 307 { 308 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n", 309 entry->e_name.bv_val, 310 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val, 311 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val); 312 return 0; 313 } 314 else if (a->a_numvals > 1) 315 { 316 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n", 317 entry->e_name.bv_val, 318 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val, 319 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val); 320 } 321 gecos=a->a_vals[0]; 322 /* get the home directory for this entry */ 323 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc); 324 if (!a) 325 { 326 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n", 327 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val ); 328 homedir=default_passwd_homeDirectory; 329 } 330 else 331 { 332 if (a->a_numvals > 1) 333 { 334 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n", 335 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val ); 336 } 337 homedir=a->a_vals[0]; 338 if (homedir.bv_val[0]=='\0') 339 homedir=default_passwd_homeDirectory; 340 } 341 /* get the shell for this entry */ 342 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc); 343 if (!a) 344 { 345 shell=default_passwd_loginShell; 346 } 347 else 348 { 349 if (a->a_numvals > 1) 350 { 351 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n", 352 entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val ); 353 } 354 shell=a->a_vals[0]; 355 if (shell.bv_val[0]=='\0') 356 shell=default_passwd_loginShell; 357 } 358 /* write the entries */ 359 for (i=0;!BER_BVISNULL(&names[i]);i++) 360 { 361 if (!isvalidusername(&names[i])) 362 { 363 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n", 364 entry->e_name.bv_val,names[i].bv_val ); 365 } 366 else 367 { 368 for (j=0;!BER_BVISNULL(&uids[j]);j++) 369 { 370 char *tmp; 371 uid_t uid; 372 uid = strtol(uids[j].bv_val, &tmp, 0); 373 if ( *tmp ) { 374 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n", 375 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val, 376 names[i].bv_val); 377 continue; 378 } 379 WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN); 380 WRITE_BERVAL(cbp->fp,&names[i]); 381 WRITE_BERVAL(cbp->fp,&passwd); 382 WRITE_INT32(cbp->fp,uid); 383 WRITE_INT32(cbp->fp,gid); 384 WRITE_BERVAL(cbp->fp,&gecos); 385 WRITE_BERVAL(cbp->fp,&homedir); 386 WRITE_BERVAL(cbp->fp,&shell); 387 } 388 } 389 } 390 return 0; 391} 392 393NSSOV_CB(passwd) 394 395NSSOV_HANDLE( 396 passwd,byname, 397 char fbuf[1024]; 398 struct berval filter = {sizeof(fbuf)}; 399 filter.bv_val = fbuf; 400 READ_STRING(fp,cbp.buf); 401 cbp.name.bv_len = tmpint32; 402 cbp.name.bv_val = cbp.buf; 403 if (!isvalidusername(&cbp.name)) { 404 Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name\n",cbp.name.bv_val); 405 return -1; 406 } 407 BER_BVZERO(&cbp.id); , 408 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val);, 409 NSLCD_ACTION_PASSWD_BYNAME, 410 nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter) 411) 412 413NSSOV_HANDLE( 414 passwd,byuid, 415 uid_t uid; 416 char fbuf[1024]; 417 struct berval filter = {sizeof(fbuf)}; 418 filter.bv_val = fbuf; 419 READ_INT32(fp,uid); 420 cbp.id.bv_val = cbp.buf; 421 cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid); 422 BER_BVZERO(&cbp.name);, 423 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)\n",cbp.id.bv_val);, 424 NSLCD_ACTION_PASSWD_BYUID, 425 nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter) 426) 427 428NSSOV_HANDLE( 429 passwd,all, 430 struct berval filter; 431 /* no parameters to read */ 432 BER_BVZERO(&cbp.name); 433 BER_BVZERO(&cbp.id);, 434 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n");, 435 NSLCD_ACTION_PASSWD_ALL, 436 (filter=cbp.mi->mi_filter,0) 437) 438