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