1 /* passwd.c - password lookup routines */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2008-2014 The OpenLDAP Foundation.
6 * Portions Copyright 2008 by Howard Chu, Symas Corp.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
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>.
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.
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 ) )
31 /* the basic search filter for searches */
32 static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)");
34 /* the attributes used in searches */
35 static struct berval passwd_keys[] = {
37 BER_BVC("userPassword"),
42 BER_BVC("homeDirectory"),
43 BER_BVC("loginShell"),
44 BER_BVC("objectClass"),
57 /* default values for attributes */
58 static struct berval default_passwd_userPassword = BER_BVC("*"); /* unmatchable */
59 static struct berval default_passwd_homeDirectory = BER_BVC("");
60 static struct berval default_passwd_loginShell = BER_BVC("");
62 static struct berval shadow_passwd = BER_BVC("x");
67 Checks to see if the specified name is a valid user name.
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
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.
79 int isvalidusername(struct berval *bv)
82 char *name = bv->bv_val;
83 if ((name==NULL)||(name[0]=='\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]=='_' ) )
91 /* check other characters */
92 for (i=1;i<bv->bv_len;i++)
96 /* if the char is $ we require it to be the last char */
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]=='-') )
106 /* no test failed so it must be good */
110 /* return 1 on success */
111 int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid)
113 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
114 AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc;
117 /* check for empty string */
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] == '=')
124 struct berval bv, 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))
131 ber_dupbv_x( uid, &bv, op->o_tmpmemctx );
134 /* look up the uid from the entry itself */
135 if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS)
137 Attribute *a = attr_find(e->e_attrs, ad);
139 ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx);
141 be_entry_release_r(op, e);
148 int nssov_name2dn_cb(Operation *op,SlapReply *rs)
150 if ( rs->sr_type == REP_SEARCH )
152 struct berval *bv = op->o_callback->sc_private;
153 if ( !BER_BVISNULL(bv)) {
154 op->o_tmpfree( bv->bv_val, op->o_tmpmemctx );
156 return LDAP_ALREADY_EXISTS;
158 ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx);
163 int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn)
165 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
167 struct berval filter = {sizeof(fbuf),fbuf};
168 slap_callback cb = {0};
169 SlapReply rs = {REP_RESULT};
173 /* if it isn't a valid username, just bail out now */
174 if (!isvalidusername(uid))
176 /* we have to look up the entry */
177 nssov_filter_byid(mi,UID_KEY,uid,&filter);
180 cb.sc_response = nssov_name2dn_cb;
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);
196 /* the maximum number of uidNumber attributes per entry */
197 #define MAXUIDS_PER_ENTRY 5
204 static struct berval shadowclass = BER_BVC("shadowAccount");
206 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
209 struct berval tmparr[2], tmpuid[2];
210 const char **tmpvalues;
212 struct berval *names;
214 struct berval passwd = {0};
217 struct berval homedir;
222 /* get the usernames for this entry */
223 if (BER_BVISNULL(&cbp->name))
225 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
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);
238 BER_BVZERO(&names[1]);
240 /* get the password for this entry */
241 a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
243 for ( i=0; i<a->a_numvals; i++) {
244 if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
252 /* if the entry has a shadowAccount entry, point to that instead */
253 passwd = shadow_passwd;
257 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
259 get_userpassword(&a->a_vals[0], &passwd);
260 if (BER_BVISNULL(&passwd))
261 passwd=default_passwd_userPassword;
263 /* get the uids for this entry */
264 if (BER_BVISNULL(&cbp->id))
266 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
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);
279 BER_BVZERO(&uids[1]);
281 /* get the gid for this entry */
282 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
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);
289 else if (a->a_numvals != 1)
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);
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'))
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);
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);
304 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
305 if (!a || !a->a_numvals)
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);
313 else if (a->a_numvals > 1)
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);
321 /* get the home directory for this entry */
322 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
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;
331 if (a->a_numvals > 1)
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);
336 homedir=a->a_vals[0];
337 if (homedir.bv_val[0]=='\0')
338 homedir=default_passwd_homeDirectory;
340 /* get the shell for this entry */
341 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
344 shell=default_passwd_loginShell;
348 if (a->a_numvals > 1)
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);
354 if (shell.bv_val[0]=='\0')
355 shell=default_passwd_loginShell;
357 /* write the entries */
358 for (i=0;!BER_BVISNULL(&names[i]);i++)
360 if (!isvalidusername(&names[i]))
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);
367 for (j=0;!BER_BVISNULL(&uids[j]);j++)
371 uid = strtol(uids[j].bv_val, &tmp, 0);
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,
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);
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);
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)
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)
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)