]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/nssov/passwd.c
20ef4d67fbf3ee51e2b9dfeced31c3ffb321a555
[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 int nssov_name2dn_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,UID_KEY,uid,&filter);
176         BER_BVZERO(dn);
177         cb.sc_private = dn;
178         cb.sc_response = nssov_name2dn_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         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 && !BER_BVISNULL(dn);
192 }
193
194 /* the maximum number of uidNumber attributes per entry */
195 #define MAXUIDS_PER_ENTRY 5
196
197 NSSOV_CBPRIV(passwd,
198         char buf[256];
199         struct berval name;
200         struct berval id;);
201
202 static struct berval shadowclass = BER_BVC("shadowAccount");
203
204 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
205 {
206         int32_t tmpint32;
207         struct berval tmparr[2], tmpuid[2];
208         const char **tmpvalues;
209         char *tmp;
210         struct berval *names;
211         struct berval *uids;
212         struct berval passwd = {0};
213         gid_t gid;
214         struct berval gecos;
215         struct berval homedir;
216         struct berval shell;
217         Attribute *a;
218         int i,j;
219         int use_shadow = 0;
220         /* get the usernames for this entry */
221         if (BER_BVISNULL(&cbp->name))
222         {
223                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
224                 if (!a)
225                 {
226                         Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
227                                 entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0);
228                         return 0;
229                 }
230                 names = a->a_vals;
231         }
232         else
233         {
234                 names=tmparr;
235                 names[0]=cbp->name;
236                 BER_BVZERO(&names[1]);
237         }
238         /* get the password for this entry */
239         a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
240         if ( a ) {
241                 for ( i=0; i<a->a_numvals; i++) {
242                         if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
243                                 use_shadow = 1;
244                                 break;
245                         }
246                 }
247         }
248         if ( use_shadow )
249         {
250                 /* if the entry has a shadowAccount entry, point to that instead */
251                 passwd = shadow_passwd;
252         }
253         else
254         {
255                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
256                 if (a)
257                         get_userpassword(&a->a_vals[0], &passwd);
258                 if (BER_BVISNULL(&passwd))
259                         passwd=default_passwd_userPassword;
260         }
261         /* get the uids for this entry */
262         if (BER_BVISNULL(&cbp->id))
263         {
264                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
265         if ( !a )
266                 {
267                         Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
268                                 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,0);
269                         return 0;
270                 }
271                 uids = a->a_vals;
272         }
273         else
274         {
275                 uids = tmpuid;
276                 uids[0] = cbp->id;
277                 BER_BVZERO(&uids[1]);
278         }
279         /* get the gid for this entry */
280         a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
281         if (!a)
282         {
283                 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
284                         entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
285                 return 0;
286         }
287         else if (a->a_numvals != 1)
288         {
289                 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
290                         entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
291         }
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'))
294         {
295                 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n",
296                         entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
297                 return 0;
298         }
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);
301         if (!a)
302                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
303         if (!a || !a->a_numvals)
304         {
305                 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n",
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);
309                 return 0;
310         }
311         else if (a->a_numvals > 1)
312         {
313                 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n",
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);
317         }
318         gecos=a->a_vals[0];
319         /* get the home directory for this entry */
320         a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
321         if (!a)
322         {
323                 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
324                         entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
325                 homedir=default_passwd_homeDirectory;
326         }
327         else
328         {
329                 if (a->a_numvals > 1)
330                 {
331                         Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
332                                 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
333                 }
334                 homedir=a->a_vals[0];
335                 if (homedir.bv_val[0]=='\0')
336                         homedir=default_passwd_homeDirectory;
337         }
338         /* get the shell for this entry */
339         a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
340         if (!a)
341         {
342                 shell=default_passwd_loginShell;
343         }
344         else
345         {
346                 if (a->a_numvals > 1)
347                 {
348                         Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
349                                 entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val,0);
350                 }
351                 shell=a->a_vals[0];
352                 if (shell.bv_val[0]=='\0')
353                         shell=default_passwd_loginShell;
354         }
355         /* write the entries */
356         for (i=0;!BER_BVISNULL(&names[i]);i++)
357         {
358                 if (!isvalidusername(&names[i]))
359                 {
360                         Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n",
361                                 entry->e_name.bv_val,names[i].bv_val,0);
362                 }
363                 else
364                 {
365                         for (j=0;!BER_BVISNULL(&uids[j]);j++)
366                         {
367                                 char *tmp;
368                                 uid_t uid;
369                                 uid = strtol(uids[j].bv_val, &tmp, 0);
370                                 if ( *tmp ) {
371                                         Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n",
372                                                 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
373                                                 names[i].bv_val);
374                                         continue;
375                                 }
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);
384                         }
385                 }
386         }
387         return 0;
388 }
389
390 NSSOV_CB(passwd)
391
392 NSSOV_HANDLE(
393         passwd,byname,
394         char fbuf[1024];
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\n",cbp.name.bv_val,0,0);
402                 return -1;
403         }
404         BER_BVZERO(&cbp.id); ,
405         Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val,0,0);,
406         NSLCD_ACTION_PASSWD_BYNAME,
407         nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
408 )
409
410 NSSOV_HANDLE(
411         passwd,byuid,
412         uid_t uid;
413         char fbuf[1024];
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)\n",cbp.id.bv_val,0,0);,
421         NSLCD_ACTION_PASSWD_BYUID,
422         nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
423 )
424
425 NSSOV_HANDLE(
426         passwd,all,
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()\n",0,0,0);,
432         NSLCD_ACTION_PASSWD_ALL,
433         (filter=cbp.mi->mi_filter,0)
434 )