]> git.sur5r.net Git - openldap/blob - contrib/ldapsasl/ldapdb.c
fix ITS#3570 (please review)
[openldap] / contrib / ldapsasl / ldapdb.c
1 /* $OpenLDAP$ */
2 /* SASL LDAP auxprop implementation
3  * Copyright (C) 2002,2003 Howard Chu, All rights reserved. <hyc@symas.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted only as authorized by the OpenLDAP
7  * Public License.
8  *
9  * A copy of this license is available in the file LICENSE in the
10  * top-level directory of the distribution or, alternatively, at
11  * <http://www.OpenLDAP.org/license.html>.
12  */
13
14 #include <config.h>
15
16 #include <stdio.h>
17
18 #include "sasl.h"
19 #include "saslutil.h"
20 #include "saslplug.h"
21
22 #ifndef SASL_VERSION_FULL
23 #define SASL_VERSION_FULL ((SASL_VERSION_MAJOR << 16) |\
24         (SASL_VERSION_MINOR << 8) |SASL_VERSION_STEP)
25 #endif
26
27 #include "plugin_common.h"
28
29 #include <ldap.h>
30
31 static char ldapdb[] = "ldapdb";
32
33 typedef struct ldapctx {
34         const char *uri;        /* URI of LDAP server */
35         struct berval id;       /* SASL authcid to bind as */
36         struct berval pw;       /* password for bind */
37         struct berval mech;     /* SASL mech */
38         int use_tls;            /* Issue StartTLS request? */
39 } ldapctx;
40
41 static int ldapdb_interact(LDAP *ld, unsigned flags __attribute__((unused)),
42         void *def, void *inter)
43 {
44         sasl_interact_t *in = inter;
45         ldapctx *ctx = def;
46         struct berval p;
47
48         for (;in->id != SASL_CB_LIST_END;in++)
49         {
50                 p.bv_val = NULL;
51                 switch(in->id)
52                 {
53                         case SASL_CB_GETREALM:
54                                 ldap_get_option(ld, LDAP_OPT_X_SASL_REALM, &p.bv_val);
55                                 if (p.bv_val) p.bv_len = strlen(p.bv_val);
56                                 break;          
57                         case SASL_CB_AUTHNAME:
58                                 p = ctx->id;
59                                 break;
60                         case SASL_CB_PASS:
61                                 p = ctx->pw;
62                                 break;
63                 }
64                 if (p.bv_val)
65                 {
66                         in->result = p.bv_val;
67                         in->len = p.bv_len;
68                 }
69         }
70         return LDAP_SUCCESS;
71 }
72
73 typedef struct connparm {
74         LDAP *ld;
75         LDAPControl c;
76         LDAPControl *ctrl[2];
77         struct berval *dn;
78 } connparm;
79
80 static int ldapdb_connect(ldapctx *ctx, sasl_server_params_t *sparams,
81         const char *user, unsigned ulen, connparm *cp)
82 {
83     int i;
84     char *authzid;
85
86     if((i=ldap_initialize(&cp->ld, ctx->uri))) {
87         return i;
88     }
89
90     authzid = sparams->utils->malloc(ulen + sizeof("u:"));
91     if (!authzid) {
92         return LDAP_NO_MEMORY;
93     } 
94     strcpy(authzid, "u:");
95     strcpy(authzid+2, user);
96     cp->c.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
97     cp->c.ldctl_value.bv_val = authzid;
98     cp->c.ldctl_value.bv_len = ulen + 2;
99     cp->c.ldctl_iscritical = 1;
100
101     i = LDAP_VERSION3;
102     ldap_set_option(cp->ld, LDAP_OPT_PROTOCOL_VERSION, &i);
103
104     /* If TLS is set and it fails, continue or bail out as requested */
105     if (ctx->use_tls && (i=ldap_start_tls_s(cp->ld, NULL, NULL)) != LDAP_SUCCESS
106         && ctx->use_tls > 1) {
107         sparams->utils->free(authzid);
108         return i;
109     }
110
111     i = ldap_sasl_interactive_bind_s(cp->ld, NULL, ctx->mech.bv_val, NULL,
112         NULL, LDAP_SASL_QUIET, ldapdb_interact, ctx);
113     if (i != LDAP_SUCCESS) {
114         sparams->utils->free(authzid);
115         return i;
116     }
117     
118     cp->ctrl[0] = &cp->c;
119     cp->ctrl[1] = NULL;
120     i = ldap_whoami_s(cp->ld, &cp->dn, cp->ctrl, NULL);
121     if (i == LDAP_SUCCESS && cp->dn) {
122         if (!cp->dn->bv_val || strncmp(cp->dn->bv_val, "dn:", 3)) {
123             ber_bvfree(cp->dn);
124             cp->dn = NULL;
125             i = LDAP_INVALID_SYNTAX;
126         } else {
127             cp->c.ldctl_value = *(cp->dn);
128         }
129     }
130     sparams->utils->free(authzid);
131     return i;
132 }
133
134 static void ldapdb_auxprop_lookup(void *glob_context,
135                                   sasl_server_params_t *sparams,
136                                   unsigned flags,
137                                   const char *user,
138                                   unsigned ulen)
139 {
140     ldapctx *ctx = glob_context;
141     connparm cp;
142     int ret, i, n, *aindx;
143     const struct propval *pr;
144     struct berval **bvals;
145     LDAPMessage *msg, *res;
146     char **attrs = NULL;
147     
148     if(!ctx || !sparams || !user) return;
149
150     pr = sparams->utils->prop_get(sparams->propctx);
151     if(!pr) return;
152
153     /* count how many attrs to fetch */
154     for(i = 0, n = 0; pr[i].name; i++) {
155         if(pr[i].name[0] == '*' && (flags & SASL_AUXPROP_AUTHZID))
156             continue;
157         if(pr[i].values && !(flags & SASL_AUXPROP_OVERRIDE))
158             continue;
159         n++;
160     }
161     /* nothing to do, bail out */
162     if (!n) return;
163
164     /* alloc an array of attr names for search, and index to the props */
165     attrs = sparams->utils->malloc((n+1)*sizeof(char *)*2);
166     if (!attrs) return;
167
168     aindx = (int *)(attrs + n + 1);
169
170     /* copy attr list */
171     for (i=0, n=0; pr[i].name; i++) {
172         if(pr[i].name[0] == '*' && (flags & SASL_AUXPROP_AUTHZID))
173             continue;
174         if(pr[i].values && !(flags & SASL_AUXPROP_OVERRIDE))
175             continue;
176         attrs[n] = (char *)pr[i].name;
177         if (pr[i].name[0] == '*') attrs[n]++;
178         aindx[n] = i;
179         n++;
180     }
181     attrs[n] = NULL;
182
183     if(ldapdb_connect(ctx, sparams, user, ulen, &cp)) {
184         goto done;
185     }
186
187     ret = ldap_search_ext_s(cp.ld, cp.dn->bv_val+3, LDAP_SCOPE_BASE,
188         "(objectclass=*)", attrs, 0, cp.ctrl, NULL, NULL, 1, &res);
189     ber_bvfree(cp.dn);
190
191     if (ret != LDAP_SUCCESS) goto done;
192
193     for(msg=ldap_first_message(cp.ld, res); msg; msg=ldap_next_message(cp.ld, msg))
194     {
195         if (ldap_msgtype(msg) != LDAP_RES_SEARCH_ENTRY) continue;
196         for (i=0; i<n; i++)
197         {
198             bvals = ldap_get_values_len(cp.ld, msg, attrs[i]);
199             if (!bvals) continue;
200             if (pr[aindx[i]].values)
201                 sparams->utils->prop_erase(sparams->propctx, pr[aindx[i]].name);
202             sparams->utils->prop_set(sparams->propctx, pr[aindx[i]].name,
203                                  bvals[0]->bv_val, bvals[0]->bv_len);
204             ber_bvecfree(bvals);
205         }
206     }
207     ldap_msgfree(res);
208
209  done:
210     if(attrs) sparams->utils->free(attrs);
211     if(cp.ld) ldap_unbind(cp.ld);
212 }
213
214 #if SASL_VERSION_FULL >= 0x020110
215 static int ldapdb_auxprop_store(void *glob_context,
216                                   sasl_server_params_t *sparams,
217                                   struct propctx *prctx,
218                                   const char *user,
219                                   unsigned ulen)
220 {
221     ldapctx *ctx = glob_context;
222     connparm cp;
223     const struct propval *pr;
224     int i, n;
225     LDAPMod **mods;
226
227     /* just checking if we are enabled */
228     if (!prctx) return SASL_OK;
229
230     if (!sparams || !user) return SASL_BADPARAM;
231
232     pr = sparams->utils->prop_get(prctx);
233     if (!pr) return SASL_BADPARAM;
234
235     for (n=0; pr[n].name; n++);
236     if (!n) return SASL_BADPARAM;
237
238     mods = sparams->utils->malloc((n+1) * sizeof(LDAPMod*) + n * sizeof(LDAPMod));
239     if (!mods) return SASL_NOMEM;
240
241     if((i=ldapdb_connect(ctx, sparams, user, ulen, &cp)) == 0) {
242
243         for (i=0; i<n; i++) {
244             mods[i] = (LDAPMod *)((char *)(mods+n+1) + i * sizeof(LDAPMod));
245             mods[i]->mod_op = LDAP_MOD_REPLACE;
246             mods[i]->mod_type = (char *)pr[i].name;
247             mods[i]->mod_values = (char **)pr[i].values;
248         }
249         mods[i] = NULL;
250
251         i = ldap_modify_ext_s(cp.ld, cp.dn->bv_val+3, mods, cp.ctrl, NULL);
252         ber_bvfree(cp.dn);
253     }
254
255     sparams->utils->free(mods);
256
257     if (i) {
258         sparams->utils->seterror(sparams->utils->conn, 0,
259             ldap_err2string(i));
260         if (i == LDAP_NO_MEMORY) i = SASL_NOMEM;
261         else i = SASL_FAIL;
262     }
263     if (cp.ld) ldap_unbind(cp.ld);
264     return i;
265 }
266 #endif /* SASL_VERSION_FULL >= 2.1.16 */
267
268 static void ldapdb_auxprop_free(void *glob_ctx, const sasl_utils_t *utils)
269 {
270         utils->free(glob_ctx);
271 }
272
273 static sasl_auxprop_plug_t ldapdb_auxprop_plugin = {
274     0,           /* Features */
275     0,           /* spare */
276     NULL,        /* glob_context */
277     ldapdb_auxprop_free,        /* auxprop_free */
278     ldapdb_auxprop_lookup,      /* auxprop_lookup */
279     ldapdb,    /* name */
280 #if SASL_VERSION_FULL >=0x020110
281     ldapdb_auxprop_store        /* spare if <2.1.16*/
282 #else
283     NULL
284 #endif
285 };
286
287 static int ldapdb_auxprop_plug_init(const sasl_utils_t *utils,
288                              int max_version,
289                              int *out_version,
290                              sasl_auxprop_plug_t **plug,
291                              const char *plugname __attribute__((unused))) 
292 {
293     ldapctx tmp, *p;
294     const char *s;
295     unsigned len;
296
297     if(!out_version || !plug) return SASL_BADPARAM;
298
299     if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
300     
301     memset(&tmp, 0, sizeof(tmp));
302
303     utils->getopt(utils->getopt_context, ldapdb, "ldapdb_uri", &tmp.uri, NULL);
304     if(!tmp.uri) return SASL_BADPARAM;
305
306     utils->getopt(utils->getopt_context, ldapdb, "ldapdb_id",
307         (const char **)&tmp.id.bv_val, &len);
308     tmp.id.bv_len = len;
309     utils->getopt(utils->getopt_context, ldapdb, "ldapdb_pw",
310         (const char **)&tmp.pw.bv_val, &len);
311     tmp.pw.bv_len = len;
312     utils->getopt(utils->getopt_context, ldapdb, "ldapdb_mech",
313         (const char **)&tmp.mech.bv_val, &len);
314     tmp.mech.bv_len = len;
315     utils->getopt(utils->getopt_context, ldapdb, "ldapdb_starttls", &s, NULL);
316     if (s)
317     {
318         if (!strcasecmp(s, "demand")) tmp.use_tls = 2;
319         else if (!strcasecmp(s, "try")) tmp.use_tls = 1;
320     }
321     utils->getopt(utils->getopt_context, ldapdb, "ldapdb_rc", &s, &len);
322     if (s)
323     {
324         char *str = utils->malloc(sizeof("LDAPRC=")+len);
325         if (!str) return SASL_NOMEM;
326         strcpy( str, "LDAPRC=" );
327         strcpy( str + sizeof("LDAPRC=")-1, s );
328         if (putenv(str))
329         {
330             utils->free(str);
331             return SASL_NOMEM;
332         }
333     }
334
335     p = utils->malloc(sizeof(ldapctx));
336     if (!p) return SASL_NOMEM;
337     *p = tmp;
338     ldapdb_auxprop_plugin.glob_context = p;
339
340     *out_version = SASL_AUXPROP_PLUG_VERSION;
341
342     *plug = &ldapdb_auxprop_plugin;
343
344     return SASL_OK;
345 }
346
347 SASL_AUXPROP_PLUG_INIT( ldapdb )
348