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,UID_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 op2.ors_tlimit = SLAP_NO_LIMIT;
188 op2.ors_slimit = SLAP_NO_LIMIT;
189 rc = op2.o_bd->be_search( &op2, &rs );
190 filter_free_x( op, op2.ors_filter, 1 );
191 return rc == LDAP_SUCCESS;
194 /* the maximum number of uidNumber attributes per entry */
195 #define MAXUIDS_PER_ENTRY 5
202 static struct berval shadowclass = BER_BVC("shadowAccount");
204 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
207 struct berval tmparr[2], tmpuid[2];
208 const char **tmpvalues;
210 struct berval *names;
212 struct berval passwd = {0};
215 struct berval homedir;
220 /* get the usernames for this entry */
221 if (BER_BVISNULL(&cbp->name))
223 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
226 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
227 entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0);
236 BER_BVZERO(&names[1]);
238 /* get the password for this entry */
239 a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
241 for ( i=0; i<a->a_numvals; i++) {
242 if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
250 /* if the entry has a shadowAccount entry, point to that instead */
251 passwd = shadow_passwd;
255 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
257 get_userpassword(&a->a_vals[0], &passwd);
258 if (BER_BVISNULL(&passwd))
259 passwd=default_passwd_userPassword;
261 /* get the uids for this entry */
262 if (BER_BVISNULL(&cbp->id))
264 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
267 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
268 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,0);
277 BER_BVZERO(&uids[1]);
279 /* get the gid for this entry */
280 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
283 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
284 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
287 else if (a->a_numvals != 1)
289 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
290 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
292 gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0);
293 if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0'))
295 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value",
296 entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
299 /* get the gecos for this entry (fall back to cn) */
300 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc);
302 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
303 if (!a || !a->a_numvals)
305 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value",
306 entry->e_name.bv_val,
307 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
308 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
311 else if (a->a_numvals > 1)
313 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values",
314 entry->e_name.bv_val,
315 cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
316 cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
319 /* get the home directory for this entry */
320 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
323 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value",
324 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
325 homedir=default_passwd_homeDirectory;
329 if (a->a_numvals > 1)
331 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
332 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
334 homedir=a->a_vals[0];
335 if (homedir.bv_val[0]=='\0')
336 homedir=default_passwd_homeDirectory;
338 /* get the shell for this entry */
339 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
342 shell=default_passwd_loginShell;
346 if (a->a_numvals > 1)
348 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values",
349 entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val,0);
352 if (shell.bv_val[0]=='\0')
353 shell=default_passwd_loginShell;
355 /* write the entries */
356 for (i=0;!BER_BVISNULL(&names[i]);i++)
358 if (!isvalidusername(&names[i]))
360 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"",
361 entry->e_name.bv_val,names[i].bv_val,0);
365 for (j=0;!BER_BVISNULL(&uids[j]);j++)
369 uid = strtol(uids[j].bv_val, &tmp, 0);
371 Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"",
372 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
376 WRITE_INT32(cbp->fp,NSLCD_RESULT_SUCCESS);
377 WRITE_BERVAL(cbp->fp,&names[i]);
378 WRITE_BERVAL(cbp->fp,&passwd);
379 WRITE_TYPE(cbp->fp,uid,uid_t);
380 WRITE_TYPE(cbp->fp,gid,gid_t);
381 WRITE_BERVAL(cbp->fp,&gecos);
382 WRITE_BERVAL(cbp->fp,&homedir);
383 WRITE_BERVAL(cbp->fp,&shell);
395 struct berval filter = {sizeof(fbuf)};
396 filter.bv_val = fbuf;
397 READ_STRING_BUF2(fp,cbp.buf,sizeof(cbp.buf));
398 cbp.name.bv_len = tmpint32;
399 cbp.name.bv_val = cbp.buf;
400 if (!isvalidusername(&cbp.name)) {
401 Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name",cbp.name.bv_val,0,0);
404 BER_BVZERO(&cbp.id); ,
405 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)",cbp.name.bv_val,0,0);,
406 NSLCD_ACTION_PASSWD_BYNAME,
407 nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
414 struct berval filter = {sizeof(fbuf)};
415 filter.bv_val = fbuf;
416 READ_TYPE(fp,uid,uid_t);
417 cbp.id.bv_val = cbp.buf;
418 cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid);
419 BER_BVZERO(&cbp.name);,
420 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)",cbp.id.bv_val,0,0);,
421 NSLCD_ACTION_PASSWD_BYUID,
422 nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
427 struct berval filter;
428 /* no parameters to read */
429 BER_BVZERO(&cbp.name);
430 BER_BVZERO(&cbp.id);,
431 Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()",0,0,0);,
432 NSLCD_ACTION_PASSWD_ALL,
433 (filter=cbp.mi->mi_filter,0)