passwd.c revision 1.1.1.1.2.2 1 /* passwd.c - password lookup routines */
2 /* $OpenLDAP: pkg/ldap/contrib/slapd-modules/nssov/passwd.c,v 1.1.2.1 2008/07/08 18:53:57 quanah Exp $ */
3 /*
4 * Copyright 2008 by Howard Chu, Symas Corp.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
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>.
14 */
15 /*
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.
19 */
20
21 #include "nssov.h"
22
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 ) )
27 */
28
29 /* the basic search filter for searches */
30 static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)");
31
32 /* the attributes used in searches */
33 static struct berval passwd_keys[] = {
34 BER_BVC("uid"),
35 BER_BVC("userPassword"),
36 BER_BVC("uidNumber"),
37 BER_BVC("gidNumber"),
38 BER_BVC("gecos"),
39 BER_BVC("cn"),
40 BER_BVC("homeDirectory"),
41 BER_BVC("loginShell"),
42 BER_BVC("objectClass"),
43 BER_BVNULL
44 };
45
46 #define UID_KEY 0
47 #define PWD_KEY 1
48 #define UIDN_KEY 2
49 #define GIDN_KEY 3
50 #define GEC_KEY 4
51 #define CN_KEY 5
52 #define DIR_KEY 6
53 #define SHL_KEY 7
54
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("");
59
60 static struct berval shadow_passwd = BER_BVC("x");
61
62 NSSOV_INIT(passwd)
63
64 /*
65 Checks to see if the specified name is a valid user name.
66
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
71
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.
76 */
77 int isvalidusername(struct berval *bv)
78 {
79 int i;
80 char *name = bv->bv_val;
81 if ((name==NULL)||(name[0]=='\0'))
82 return 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]=='_' ) )
88 return 0;
89 /* check other characters */
90 for (i=1;i<bv->bv_len;i++)
91 {
92 if ( name[i]=='$' )
93 {
94 /* if the char is $ we require it to be the last char */
95 if (name[i+1]!='\0')
96 return 0;
97 }
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]=='-') )
102 return 0;
103 }
104 /* no test failed so it must be good */
105 return -1;
106 }
107
108 /* return 1 on success */
109 int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid)
110 {
111 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
112 AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc;
113 Entry *e;
114
115 /* check for empty string */
116 if (!dn->bv_len)
117 return 0;
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] == '=')
121 {
122 struct berval bv, rdn;
123 dnRdn(dn, &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))
128 return 0;
129 ber_dupbv_x( uid, &bv, op->o_tmpmemctx );
130 return 1;
131 }
132 /* look up the uid from the entry itself */
133 if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS)
134 {
135 Attribute *a = attr_find(e->e_attrs, ad);
136 if (a) {
137 ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx);
138 }
139 be_entry_release_r(op, e);
140 if (a)
141 return 1;
142 }
143 return 0;
144 }
145
146 static int uid2dn_cb(Operation *op,SlapReply *rs)
147 {
148 if ( rs->sr_type == REP_SEARCH )
149 {
150 struct berval *bv = op->o_callback->sc_private;
151 if ( !BER_BVISNULL(bv)) {
152 op->o_tmpfree( bv->bv_val, op->o_tmpmemctx );
153 BER_BVZERO(bv);
154 return LDAP_ALREADY_EXISTS;
155 }
156 ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx);
157 }
158 return LDAP_SUCCESS;
159 }
160
161 int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn)
162 {
163 nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
164 char fbuf[1024];
165 struct berval filter = {sizeof(fbuf),fbuf};
166 slap_callback cb = {0};
167 SlapReply rs = {REP_RESULT};
168 Operation op2;
169 int rc;
170
171 /* if it isn't a valid username, just bail out now */
172 if (!isvalidusername(uid))
173 return 0;
174 /* we have to look up the entry */
175 nssov_filter_byid(mi,UIDN_KEY,uid,&filter);
176 BER_BVZERO(dn);
177 cb.sc_private = dn;
178 cb.sc_response = uid2dn_cb;
179 op2 = *op;
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;
190 }
191
192 /* the maximum number of uidNumber attributes per entry */
193 #define MAXUIDS_PER_ENTRY 5
194
195 NSSOV_CBPRIV(passwd,
196 char buf[256];
197 struct berval name;
198 struct berval id;);
199
200 static struct berval shadowclass = BER_BVC("shadowAccount");
201
202 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
203 {
204 int32_t tmpint32;
205 struct berval tmparr[2], tmpuid[2];
206 const char **tmpvalues;
207 char *tmp;
208 struct berval *names;
209 struct berval *uids;
210 struct berval passwd = {0};
211 gid_t gid;
212 struct berval gecos;
213 struct berval homedir;
214 struct berval shell;
215 Attribute *a;
216 int i,j;
217 int use_shadow = 0;
218 /* get the usernames for this entry */
219 if (BER_BVISNULL(&cbp->name))
220 {
221 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
222 if (!a)
223 {
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);
226 return 0;
227 }
228 names = a->a_vals;
229 }
230 else
231 {
232 names=tmparr;
233 names[0]=cbp->name;
234 BER_BVZERO(&names[1]);
235 }
236 /* get the password for this entry */
237 a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
238 if ( a ) {
239 for ( i=0; i<a->a_numvals; i++) {
240 if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
241 use_shadow = 1;
242 break;
243 }
244 }
245 }
246 if ( use_shadow )
247 {
248 /* if the entry has a shadowAccount entry, point to that instead */
249 passwd = shadow_passwd;
250 }
251 else
252 {
253 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
254 if (a)
255 get_userpassword(&a->a_vals[0], &passwd);
256 if (BER_BVISNULL(&passwd))
257 passwd=default_passwd_userPassword;
258 }
259 /* get the uids for this entry */
260 if (BER_BVISNULL(&cbp->id))
261 {
262 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
263 if ( !a )
264 {
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);
267 return 0;
268 }
269 uids = a->a_vals;
270 }
271 else
272 {
273 uids = tmpuid;
274 uids[0] = cbp->id;
275 BER_BVZERO(&uids[1]);
276 }
277 /* get the gid for this entry */
278 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
279 if (!a)
280 {
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);
283 return 0;
284 }
285 else if (a->a_numvals != 1)
286 {
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);
289 }
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'))
292 {
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);
295 return 0;
296 }
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);
299 if (!a)
300 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
301 if (!a || !a->a_numvals)
302 {
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);
307 return 0;
308 }
309 else if (a->a_numvals > 1)
310 {
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);
315 }
316 gecos=a->a_vals[0];
317 /* get the home directory for this entry */
318 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
319 if (!a)
320 {
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;
324 }
325 else
326 {
327 if (a->a_numvals > 1)
328 {
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);
331 }
332 homedir=a->a_vals[0];
333 if (homedir.bv_val[0]=='\0')
334 homedir=default_passwd_homeDirectory;
335 }
336 /* get the shell for this entry */
337 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
338 if (!a)
339 {
340 shell=default_passwd_loginShell;
341 }
342 else
343 {
344 if (a->a_numvals > 1)
345 {
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);
348 }
349 shell=a->a_vals[0];
350 if (shell.bv_val[0]=='\0')
351 shell=default_passwd_loginShell;
352 }
353 /* write the entries */
354 for (i=0;!BER_BVISNULL(&names[i]);i++)
355 {
356 if (!isvalidusername(&names[i]))
357 {
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);
360 }
361 else
362 {
363 for (j=0;!BER_BVISNULL(&uids[j]);j++)
364 {
365 char *tmp;
366 uid_t uid;
367 uid = strtol(uids[j].bv_val, &tmp, 0);
368 if ( *tmp ) {
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,
371 names[i].bv_val);
372 continue;
373 }
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);
382 }
383 }
384 }
385 return 0;
386 }
387
388 NSSOV_CB(passwd)
389
390 NSSOV_HANDLE(
391 passwd,byname,
392 char fbuf[1024];
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);
400 return -1;
401 }
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)
406 )
407
408 NSSOV_HANDLE(
409 passwd,byuid,
410 uid_t uid;
411 char fbuf[1024];
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)
421 )
422
423 NSSOV_HANDLE(
424 passwd,all,
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)
432 )
433