]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/nssov/passwd.c
ITS#5801
[openldap] / contrib / slapd-modules / nssov / passwd.c
1 /* passwd.c - password lookup routines */
2 /* $OpenLDAP$ */
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, 1 );
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 )