]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/nssov/passwd.c
Merge remote-tracking branch 'origin/mdb.master'
[openldap] / contrib / slapd-modules / nssov / passwd.c
1 /* passwd.c - password lookup routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 
4  *
5  * Copyright 2008-2014 The OpenLDAP Foundation.
6  * Portions Copyright 2008 by Howard Chu, Symas Corp.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This code references portions of the nss-ldapd package
19  * written by Arthur de Jong. The nss-ldapd code was forked
20  * from the nss-ldap library written by Luke Howard.
21  */
22
23 #include "nssov.h"
24
25 /* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
26  *       DESC 'Abstraction of an account with POSIX attributes'
27  *       MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
28  *       MAY ( userPassword $ loginShell $ gecos $ description ) )
29  */
30
31 /* the basic search filter for searches */
32 static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)");
33
34 /* the attributes used in searches */
35 static struct berval passwd_keys[] = {
36         BER_BVC("uid"),
37         BER_BVC("userPassword"),
38         BER_BVC("uidNumber"),
39         BER_BVC("gidNumber"),
40         BER_BVC("gecos"),
41         BER_BVC("cn"),
42         BER_BVC("homeDirectory"),
43         BER_BVC("loginShell"),
44         BER_BVC("objectClass"),
45         BER_BVNULL
46 };
47
48 #define UID_KEY 0
49 #define PWD_KEY 1
50 #define UIDN_KEY        2
51 #define GIDN_KEY        3
52 #define GEC_KEY 4
53 #define CN_KEY  5
54 #define DIR_KEY 6
55 #define SHL_KEY 7
56
57 /* default values for attributes */
58 static struct berval default_passwd_userPassword        = BER_BVC("*"); /* unmatchable */
59 static struct berval default_passwd_homeDirectory       = BER_BVC("");
60 static struct berval default_passwd_loginShell          = BER_BVC("");
61
62 static struct berval shadow_passwd = BER_BVC("x");
63
64 NSSOV_INIT(passwd)
65
66 /*
67          Checks to see if the specified name is a valid user name.
68
69          This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
70          and 3.276 Portable Filename Character Set):
71          http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
72          http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
73
74          The standard defines user names valid if they contain characters from
75          the set [A-Za-z0-9._-] where the hyphen should not be used as first
76          character. As an extension this test allows the dolar '$' sign as the last
77          character to support Samba special accounts.
78 */
79 int isvalidusername(struct berval *bv)
80 {
81         int i;
82         char *name = bv->bv_val;
83         if ((name==NULL)||(name[0]=='\0'))
84                 return 0;
85         /* check first character */
86         if ( ! ( (name[0]>='A' && name[0] <= 'Z') ||
87                                          (name[0]>='a' && name[0] <= 'z') ||
88                                          (name[0]>='0' && name[0] <= '9') ||
89                                          name[0]=='.' || name[0]=='_' ) )
90                 return 0;
91         /* check other characters */
92         for (i=1;i<bv->bv_len;i++)
93         {
94                 if ( name[i]=='$' )
95                 {
96                         /* if the char is $ we require it to be the last char */
97                         if (name[i+1]!='\0')
98                                 return 0;
99                 }
100                 else if ( ! ( (name[i]>='A' && name[i] <= 'Z') ||
101                                                                         (name[i]>='a' && name[i] <= 'z') ||
102                                                                         (name[i]>='0' && name[i] <= '9') ||
103                                                                         name[i]=='.' || name[i]=='_'    || name[i]=='-') )
104                         return 0;
105         }
106         /* no test failed so it must be good */
107         return -1;
108 }
109
110 /* return 1 on success */
111 int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid)
112 {
113         nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
114         AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc;
115         Entry *e;
116
117         /* check for empty string */
118         if (!dn->bv_len)
119                 return 0;
120         /* try to look up uid within DN string */
121         if (!strncmp(dn->bv_val,ad->ad_cname.bv_val,ad->ad_cname.bv_len) &&
122                 dn->bv_val[ad->ad_cname.bv_len] == '=')
123         {
124                 struct berval bv, rdn;
125                 dnRdn(dn, &rdn);
126                 /* check if it is valid */
127                 bv.bv_val = dn->bv_val + ad->ad_cname.bv_len + 1;
128                 bv.bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
129                 if (!isvalidusername(&bv))
130                         return 0;
131                 ber_dupbv_x( uid, &bv, op->o_tmpmemctx );
132                 return 1;
133         }
134         /* look up the uid from the entry itself */
135         if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS)
136         {
137                 Attribute *a = attr_find(e->e_attrs, ad);
138                 if (a) {
139                         ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx);
140                 }
141                 be_entry_release_r(op, e);
142                 if (a)
143                         return 1;
144         }
145         return 0;
146 }
147
148 int nssov_name2dn_cb(Operation *op,SlapReply *rs)
149 {
150         if ( rs->sr_type == REP_SEARCH )
151         {
152                 struct berval *bv = op->o_callback->sc_private;
153                 if ( !BER_BVISNULL(bv)) {
154                         op->o_tmpfree( bv->bv_val, op->o_tmpmemctx );
155                         BER_BVZERO(bv);
156                         return LDAP_ALREADY_EXISTS;
157                 }
158                 ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx);
159         }
160         return LDAP_SUCCESS;
161 }
162
163 int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn)
164 {
165         nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
166         char fbuf[1024];
167         struct berval filter = {sizeof(fbuf),fbuf};
168         slap_callback cb = {0};
169         SlapReply rs = {REP_RESULT};
170         Operation op2;
171         int rc;
172
173         /* if it isn't a valid username, just bail out now */
174         if (!isvalidusername(uid))
175                 return 0;
176         /* we have to look up the entry */
177         nssov_filter_byid(mi,UID_KEY,uid,&filter);
178         BER_BVZERO(dn);
179         cb.sc_private = dn;
180         cb.sc_response = nssov_name2dn_cb;
181         op2 = *op;
182         op2.o_callback = &cb;
183         op2.o_req_dn = mi->mi_base;
184         op2.o_req_ndn = mi->mi_base;
185         op2.ors_scope = mi->mi_scope;
186         op2.ors_filterstr = filter;
187         op2.ors_filter = str2filter_x( op, filter.bv_val );
188         op2.ors_attrs = slap_anlist_no_attrs;
189         op2.ors_tlimit = SLAP_NO_LIMIT;
190         op2.ors_slimit = SLAP_NO_LIMIT;
191         rc = op2.o_bd->be_search( &op2, &rs );
192         filter_free_x( op, op2.ors_filter, 1 );
193         return rc == LDAP_SUCCESS && !BER_BVISNULL(dn);
194 }
195
196 /* the maximum number of uidNumber attributes per entry */
197 #define MAXUIDS_PER_ENTRY 5
198
199 NSSOV_CBPRIV(passwd,
200         char buf[256];
201         struct berval name;
202         struct berval id;);
203
204 static struct berval shadowclass = BER_BVC("shadowAccount");
205
206 static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
207 {
208         int32_t tmpint32;
209         struct berval tmparr[2], tmpuid[2];
210         const char **tmpvalues;
211         char *tmp;
212         struct berval *names;
213         struct berval *uids;
214         struct berval passwd = {0};
215         gid_t gid;
216         struct berval gecos;
217         struct berval homedir;
218         struct berval shell;
219         Attribute *a;
220         int i,j;
221         int use_shadow = 0;
222         /* get the usernames for this entry */
223         if (BER_BVISNULL(&cbp->name))
224         {
225                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
226                 if (!a)
227                 {
228                         Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
229                                 entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,0);
230                         return 0;
231                 }
232                 names = a->a_vals;
233         }
234         else
235         {
236                 names=tmparr;
237                 names[0]=cbp->name;
238                 BER_BVZERO(&names[1]);
239         }
240         /* get the password for this entry */
241         a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
242         if ( a ) {
243                 for ( i=0; i<a->a_numvals; i++) {
244                         if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
245                                 use_shadow = 1;
246                                 break;
247                         }
248                 }
249         }
250         if ( use_shadow )
251         {
252                 /* if the entry has a shadowAccount entry, point to that instead */
253                 passwd = shadow_passwd;
254         }
255         else
256         {
257                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
258                 if (a)
259                         get_userpassword(&a->a_vals[0], &passwd);
260                 if (BER_BVISNULL(&passwd))
261                         passwd=default_passwd_userPassword;
262         }
263         /* get the uids for this entry */
264         if (BER_BVISNULL(&cbp->id))
265         {
266                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
267         if ( !a )
268                 {
269                         Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
270                                 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,0);
271                         return 0;
272                 }
273                 uids = a->a_vals;
274         }
275         else
276         {
277                 uids = tmpuid;
278                 uids[0] = cbp->id;
279                 BER_BVZERO(&uids[1]);
280         }
281         /* get the gid for this entry */
282         a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
283         if (!a)
284         {
285                 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
286                         entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
287                 return 0;
288         }
289         else if (a->a_numvals != 1)
290         {
291                 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
292                         entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
293         }
294         gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0);
295         if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0'))
296         {
297                 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n",
298                         entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val,0);
299                 return 0;
300         }
301         /* get the gecos for this entry (fall back to cn) */
302         a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc);
303         if (!a)
304                 a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
305         if (!a || !a->a_numvals)
306         {
307                 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n",
308                         entry->e_name.bv_val,
309                         cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
310                         cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
311                 return 0;
312         }
313         else if (a->a_numvals > 1)
314         {
315                 Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n",
316                         entry->e_name.bv_val,
317                         cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
318                         cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
319         }
320         gecos=a->a_vals[0];
321         /* get the home directory for this entry */
322         a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
323         if (!a)
324         {
325                 Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
326                         entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
327                 homedir=default_passwd_homeDirectory;
328         }
329         else
330         {
331                 if (a->a_numvals > 1)
332                 {
333                         Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
334                                 entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val,0);
335                 }
336                 homedir=a->a_vals[0];
337                 if (homedir.bv_val[0]=='\0')
338                         homedir=default_passwd_homeDirectory;
339         }
340         /* get the shell for this entry */
341         a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
342         if (!a)
343         {
344                 shell=default_passwd_loginShell;
345         }
346         else
347         {
348                 if (a->a_numvals > 1)
349                 {
350                         Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
351                                 entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val,0);
352                 }
353                 shell=a->a_vals[0];
354                 if (shell.bv_val[0]=='\0')
355                         shell=default_passwd_loginShell;
356         }
357         /* write the entries */
358         for (i=0;!BER_BVISNULL(&names[i]);i++)
359         {
360                 if (!isvalidusername(&names[i]))
361                 {
362                         Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n",
363                                 entry->e_name.bv_val,names[i].bv_val,0);
364                 }
365                 else
366                 {
367                         for (j=0;!BER_BVISNULL(&uids[j]);j++)
368                         {
369                                 char *tmp;
370                                 uid_t uid;
371                                 uid = strtol(uids[j].bv_val, &tmp, 0);
372                                 if ( *tmp ) {
373                                         Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n",
374                                                 entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
375                                                 names[i].bv_val);
376                                         continue;
377                                 }
378                                 WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
379                                 WRITE_BERVAL(cbp->fp,&names[i]);
380                                 WRITE_BERVAL(cbp->fp,&passwd);
381                                 WRITE_TYPE(cbp->fp,uid,uid_t);
382                                 WRITE_TYPE(cbp->fp,gid,gid_t);
383                                 WRITE_BERVAL(cbp->fp,&gecos);
384                                 WRITE_BERVAL(cbp->fp,&homedir);
385                                 WRITE_BERVAL(cbp->fp,&shell);
386                         }
387                 }
388         }
389         return 0;
390 }
391
392 NSSOV_CB(passwd)
393
394 NSSOV_HANDLE(
395         passwd,byname,
396         char fbuf[1024];
397         struct berval filter = {sizeof(fbuf)};
398         filter.bv_val = fbuf;
399         READ_STRING(fp,cbp.buf);
400         cbp.name.bv_len = tmpint32;
401         cbp.name.bv_val = cbp.buf;
402         if (!isvalidusername(&cbp.name)) {
403                 Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name\n",cbp.name.bv_val,0,0);
404                 return -1;
405         }
406         BER_BVZERO(&cbp.id); ,
407         Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val,0,0);,
408         NSLCD_ACTION_PASSWD_BYNAME,
409         nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
410 )
411
412 NSSOV_HANDLE(
413         passwd,byuid,
414         uid_t uid;
415         char fbuf[1024];
416         struct berval filter = {sizeof(fbuf)};
417         filter.bv_val = fbuf;
418         READ_TYPE(fp,uid,uid_t);
419         cbp.id.bv_val = cbp.buf;
420         cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid);
421         BER_BVZERO(&cbp.name);,
422         Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)\n",cbp.id.bv_val,0,0);,
423         NSLCD_ACTION_PASSWD_BYUID,
424         nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
425 )
426
427 NSSOV_HANDLE(
428         passwd,all,
429         struct berval filter;
430         /* no parameters to read */
431         BER_BVZERO(&cbp.name);
432         BER_BVZERO(&cbp.id);,
433         Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n",0,0,0);,
434         NSLCD_ACTION_PASSWD_ALL,
435         (filter=cbp.mi->mi_filter,0)
436 )