1 /* passwd.c - password lookup routines */
4 * Copyright 2008 by Howard Chu, Symas Corp.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
16 * This code references portions of the nss-ldapd package
17 * written by Arthur de Jong. The nss-ldapd code was forked
18 * from the nss-ldap library written by Luke Howard.
23 /* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
24 * DESC 'Abstraction of an account with POSIX attributes'
25 * MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
26 * MAY ( userPassword $ loginShell $ gecos $ description ) )
29 /* the basic search filter for searches */
30 static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)");
32 /* the attributes used in searches */
33 static struct berval passwd_keys[] = {
35 BER_BVC("userPassword"),
40 BER_BVC("homeDirectory"),
41 BER_BVC("loginShell"),
42 BER_BVC("objectClass"),
55 /* default values for attributes */
56 static struct berval default_passwd_userPassword = BER_BVC("*"); /* unmatchable */
57 static struct berval default_passwd_homeDirectory = BER_BVC("");
58 static struct berval default_passwd_loginShell = BER_BVC("");
60 static struct berval shadow_passwd = BER_BVC("x");
65 Checks to see if the specified name is a valid user name.
67 This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
68 and 3.276 Portable Filename Character Set):
69 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
70 http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
72 The standard defines user names valid if they contain characters from
73 the set [A-Za-z0-9._-] where the hyphen should not be used as first
74 character. As an extension this test allows the dolar '$' sign as the last
75 character to support Samba special accounts.
77 int isvalidusername(struct berval *bv)
80 char *name = bv->bv_val;
81 if ((name==NULL)||(name[0]=='\0'))
83 /* check first character */
84 if ( ! ( (name[0]>='A' && name[0] <= 'Z') ||
85 (name[0]>='a' && name[0] <= 'z') ||
86 (name[0]>='0' && name[0] <= '9') ||
87 name[0]=='.' || name[0]=='_' ) )
89 /* check other characters */
90 for (i=1;i<bv->bv_len;i++)
94 /* if the char is $ we require it to be the last char */
98 else if ( ! ( (name[i]>='A' && name[i] <= 'Z') ||
99 (name[i]>='a' && name[i] <= 'z') ||
100 (name[i]>='0' && name[i] <= '9') ||
101 name[i]=='.' || name[i]=='_' || name[i]=='-') )
104 /* no test failed so it must be good */
108 /* return 1 on success */
109 int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid)
111 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
112 AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc;
115 /* check for empty string */
118 /* try to look up uid within DN string */
119 if (!strncmp(dn->bv_val,ad->ad_cname.bv_val,ad->ad_cname.bv_len) &&
120 dn->bv_val[ad->ad_cname.bv_len] == '=')
122 struct berval bv, rdn;
124 /* check if it is valid */
125 bv.bv_val = dn->bv_val + ad->ad_cname.bv_len + 1;
126 bv.bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
127 if (!isvalidusername(&bv))
129 ber_dupbv_x( uid, &bv, op->o_tmpmemctx );
132 /* look up the uid from the entry itself */
133 if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS)
135 Attribute *a = attr_find(e->e_attrs, ad);
137 ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx);
139 be_entry_release_r(op, e);
146 static int uid2dn_cb(Operation *op,SlapReply *rs)
148 if ( rs->sr_type == REP_SEARCH )
150 struct berval *bv = op->o_callback->sc_private;
151 if ( !BER_BVISNULL(bv)) {
152 op->o_tmpfree( bv->bv_val, op->o_tmpmemctx );
154 return LDAP_ALREADY_EXISTS;
156 ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx);
161 int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn)
163 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
165 struct berval filter = {sizeof(fbuf),fbuf};
166 slap_callback cb = {0};
167 SlapReply rs = {REP_RESULT};
171 /* if it isn't a valid username, just bail out now */
172 if (!isvalidusername(uid))
174 /* we have to look up the entry */
175 nssov_filter_byid(mi,UIDN_KEY,uid,&filter);
178 cb.sc_response = uid2dn_cb;
180 op2.o_callback = &cb;
181 op2.o_req_dn = mi->mi_base;
182 op2.o_req_ndn = mi->mi_base;
183 op2.ors_scope = mi->mi_scope;
184 op2.ors_filterstr = filter;
185 op2.ors_filter = str2filter_x( op, filter.bv_val );
186 op2.ors_attrs = slap_anlist_no_attrs;
187 rc = op2.o_bd->be_search( &op2, &rs );
188 filter_free_x( op, op2.ors_filter );
189 return rc == LDAP_SUCCESS;
192 /* the maximum number of uidNumber attributes per entry */
193 #define MAXUIDS_PER_ENTRY 5
200 static struct berval shadowclass = BER_BVC("shadowAccount");
202 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
205 struct berval tmparr[2], tmpuid[2];
206 const char **tmpvalues;
208 struct berval *names;
210 struct berval passwd = {0};
213 struct berval homedir;
218 /* get the usernames for this entry */
219 if (BER_BVISNULL(&cbp->name))
221 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
224 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
225 entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0);
234 BER_BVZERO(&names[1]);
236 /* get the password for this entry */
237 a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
239 for ( i=0; i<a->a_numvals; i++) {
240 if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
248 /* if the entry has a shadowAccount entry, point to that instead */
249 passwd = shadow_passwd;
253 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
255 get_userpassword(&a->a_vals[0], &passwd);
256 if (BER_BVISNULL(&passwd))
257 passwd=default_passwd_userPassword;
259 /* get the uids for this entry */
260 if (BER_BVISNULL(&cbp->id))
262 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
265 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
266 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,0);
275 BER_BVZERO(&uids[1]);
277 /* get the gid for this entry */
278 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
281 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
282 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
285 else if (a->a_numvals != 1)
287 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
288 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
290 gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0);
291 if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0'))
293 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value",
294 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
297 /* get the gecos for this entry (fall back to cn) */
298 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc);
300 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
301 if (!a || !a->a_numvals)
303 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value",
304 entry->e_name.bv_val,
305 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
306 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
309 else if (a->a_numvals > 1)
311 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values",
312 entry->e_name.bv_val,
313 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
314 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
317 /* get the home directory for this entry */
318 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
321 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
322 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
323 homedir=default_passwd_homeDirectory;
327 if (a->a_numvals > 1)
329 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
330 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
332 homedir=a->a_vals[0];
333 if (homedir.bv_val[0]=='\0')
334 homedir=default_passwd_homeDirectory;
336 /* get the shell for this entry */
337 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
340 shell=default_passwd_loginShell;
344 if (a->a_numvals > 1)
346 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
347 entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val,0);
350 if (shell.bv_val[0]=='\0')
351 shell=default_passwd_loginShell;
353 /* write the entries */
354 for (i=0;!BER_BVISNULL(&names[i]);i++)
356 if (!isvalidusername(&names[i]))
358 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"",
359 entry->e_name.bv_val,names[i].bv_val,0);
363 for (j=0;!BER_BVISNULL(&uids[j]);j++)
367 uid = strtol(uids[j].bv_val, &tmp, 0);
369 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"",
370 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
374 WRITE_INT32(cbp->fp,NSLCD_RESULT_SUCCESS);
375 WRITE_BERVAL(cbp->fp,&names[i]);
376 WRITE_BERVAL(cbp->fp,&passwd);
377 WRITE_TYPE(cbp->fp,uid,uid_t);
378 WRITE_TYPE(cbp->fp,gid,gid_t);
379 WRITE_BERVAL(cbp->fp,&gecos);
380 WRITE_BERVAL(cbp->fp,&homedir);
381 WRITE_BERVAL(cbp->fp,&shell);
393 struct berval filter = {sizeof(fbuf)};
394 filter.bv_val = fbuf;
395 READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
396 cbp.name.bv_len = tmpint32;
397 cbp.name.bv_val = cbp.buf;
398 if (!isvalidusername(&cbp.name)) {
399 Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name",cbp.name.bv_val,0,0);
402 BER_BVZERO(&cbp.id); ,
403 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)",cbp.name.bv_val,0,0);,
404 NSLCD_ACTION_PASSWD_BYNAME,
405 nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
412 struct berval filter = {sizeof(fbuf)};
413 filter.bv_val = fbuf;
414 READ_TYPE(fp,uid,uid_t);
415 cbp.id.bv_val = cbp.buf;
416 cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid);
417 BER_BVZERO(&cbp.name);,
418 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)",cbp.id.bv_val,0,0);,
419 NSLCD_ACTION_PASSWD_BYUID,
420 nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
425 struct berval filter;
426 /* no parameters to read */
427 BER_BVZERO(&cbp.name);
428 BER_BVZERO(&cbp.id);,
429 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()",0,0,0);,
430 NSLCD_ACTION_PASSWD_ALL,
431 (filter=cbp.mi->mi_filter,0)