GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / lib / canonusr.c
1 /* canonusr.c - user canonicalization support
2  * Rob Siemborski
3  * $Id: canonusr.c,v 1.15 2004/02/20 23:54:51 rjs3 Exp $
4  */
5 /* 
6  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer. 
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The name "Carnegie Mellon University" must not be used to
21  *    endorse or promote products derived from this software without
22  *    prior written permission. For permission or any other legal
23  *    details, please contact  
24  *      Office of Technology Transfer
25  *      Carnegie Mellon University
26  *      5000 Forbes Avenue
27  *      Pittsburgh, PA  15213-3890
28  *      (412) 268-4387, fax: (412) 268-7395
29  *      tech-transfer@andrew.cmu.edu
30  *
31  * 4. Redistributions of any form whatsoever must retain the following
32  *    acknowledgment:
33  *    "This product includes software developed by Computing Services
34  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35  *
36  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43  */
44
45 #include <config.h>
46 #include <sasl.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <prop.h>
50 #include <stdio.h>
51
52 #include "saslint.h"
53
54 typedef struct canonuser_plug_list 
55 {
56     struct canonuser_plug_list *next;
57     char name[PATH_MAX];
58     const sasl_canonuser_plug_t *plug;
59 } canonuser_plug_list_t;
60
61 static canonuser_plug_list_t *canonuser_head = NULL;
62
63 /* default behavior:
64  *                   eliminate leading & trailing whitespace,
65  *                   null-terminate, and get into the outparams
66  *
67  *                   (handled by INTERNAL plugin) */
68 /* Also does auxprop lookups once username is canonicalized */
69 /* a zero ulen or alen indicates that it is strlen(value) */
70 int _sasl_canon_user(sasl_conn_t *conn,
71                      const char *user, unsigned ulen,
72                      unsigned flags,
73                      sasl_out_params_t *oparams)
74 {
75     canonuser_plug_list_t *ptr;
76     sasl_server_conn_t *sconn = NULL;
77     sasl_client_conn_t *cconn = NULL;
78     sasl_canon_user_t *cuser_cb;
79     sasl_getopt_t *getopt;
80     void *context;
81     int result;
82     const char *plugin_name = NULL;
83     char *user_buf;
84     unsigned *lenp;
85
86     if(!conn) return SASL_BADPARAM;    
87     if(!user || !oparams) return SASL_BADPARAM;
88
89     if(flags & SASL_CU_AUTHID) {
90         user_buf = conn->authid_buf;
91         lenp = &(oparams->alen);
92     } else if (flags & SASL_CU_AUTHZID) {
93         user_buf = conn->user_buf;
94         lenp = &(oparams->ulen);
95     } else {
96         return SASL_BADPARAM;
97     }
98     
99     if(conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn;
100     else if(conn->type == SASL_CONN_CLIENT) cconn = (sasl_client_conn_t *)conn;
101     else return SASL_FAIL;
102     
103     if(!ulen) ulen = (unsigned int)strlen(user);
104     
105     /* check to see if we have a callback to make*/
106     result = _sasl_getcallback(conn, SASL_CB_CANON_USER,
107                                &cuser_cb, &context);
108     if(result == SASL_OK && cuser_cb) {
109         result = cuser_cb(conn, context,
110                         user, ulen,
111                         flags, (conn->type == SASL_CONN_SERVER ?
112                                 ((sasl_server_conn_t *)conn)->user_realm :
113                                 NULL),
114                         user_buf, CANON_BUF_SIZE, lenp);
115         
116
117         if (result != SASL_OK) return result;
118
119         /* Point the input copy at the stored buffer */
120         user = user_buf;
121         ulen = *lenp;
122     }
123
124     /* which plugin are we supposed to use? */
125     result = _sasl_getcallback(conn, SASL_CB_GETOPT,
126                                &getopt, &context);
127     if(result == SASL_OK && getopt) {
128         getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL);
129     }
130
131     if(!plugin_name) {
132         /* Use Defualt */
133         plugin_name = "INTERNAL";
134     }
135     
136     for(ptr = canonuser_head; ptr; ptr = ptr->next) {
137         /* A match is if we match the internal name of the plugin, or if
138          * we match the filename (old-style) */
139         if((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name))
140            || !strcmp(plugin_name, ptr->name)) break;
141     }
142
143     /* We clearly don't have this one! */
144     if(!ptr) {
145         sasl_seterror(conn, 0, "desired canon_user plugin %s not found",
146                       plugin_name);
147         return SASL_NOMECH;
148     }
149     
150     if(sconn) {
151         /* we're a server */
152         result = ptr->plug->canon_user_server(ptr->plug->glob_context,
153                                               sconn->sparams,
154                                               user, ulen,
155                                               flags,
156                                               user_buf,
157                                               CANON_BUF_SIZE, lenp);
158     } else {
159         /* we're a client */
160         result = ptr->plug->canon_user_client(ptr->plug->glob_context,
161                                               cconn->cparams,
162                                               user, ulen,
163                                               flags,
164                                               user_buf,
165                                               CANON_BUF_SIZE, lenp);
166     }
167
168     if(result != SASL_OK) return result;
169
170     if((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) {
171         /* We did both, so we need to copy the result into
172          * the buffer for the authzid from the buffer for the authid */
173         memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE);
174         oparams->ulen = oparams->alen;
175     }
176         
177     /* Set the appropriate oparams (lengths have already been set by lenp) */
178     if(flags & SASL_CU_AUTHID) {
179         oparams->authid = conn->authid_buf;
180     }
181
182     if (flags & SASL_CU_AUTHZID) {
183         oparams->user = conn->user_buf;
184     }
185
186 #ifndef macintosh
187     /* do auxprop lookups (server only) */
188     if(sconn) {
189         if(flags & SASL_CU_AUTHID) {
190             _sasl_auxprop_lookup(sconn->sparams, 0,
191                                  oparams->authid, oparams->alen);
192         }
193         if(flags & SASL_CU_AUTHZID) {
194             _sasl_auxprop_lookup(sconn->sparams, SASL_AUXPROP_AUTHZID,
195                                  oparams->user, oparams->ulen);
196         }
197     }
198 #endif
199
200
201     RETURN(conn, SASL_OK);
202 }
203
204 void _sasl_canonuser_free() 
205 {
206     canonuser_plug_list_t *ptr, *ptr_next;
207     
208     for(ptr = canonuser_head; ptr; ptr = ptr_next) {
209         ptr_next = ptr->next;
210         if(ptr->plug->canon_user_free)
211             ptr->plug->canon_user_free(ptr->plug->glob_context,
212                                        sasl_global_utils);
213         sasl_FREE(ptr);
214     }
215
216     canonuser_head = NULL;
217 }
218
219 int sasl_canonuser_add_plugin(const char *plugname,
220                               sasl_canonuser_init_t *canonuserfunc) 
221 {
222     int result, out_version;
223     canonuser_plug_list_t *new_item;
224     sasl_canonuser_plug_t *plug;
225
226     if(!plugname || strlen(plugname) > (PATH_MAX - 1)) {
227         sasl_seterror(NULL, 0,
228                       "bad plugname passed to sasl_canonuser_add_plugin\n");
229         return SASL_BADPARAM;
230     }
231     
232     result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION,
233                            &out_version, &plug, plugname);
234
235     if(result != SASL_OK) {
236         _sasl_log(NULL, SASL_LOG_ERR, "canonuserfunc error %i\n",result);
237         return result;
238     }
239
240     if(!plug->canon_user_server && !plug->canon_user_client) {
241         /* We need at least one of these implemented */
242         _sasl_log(NULL, SASL_LOG_ERR,
243                   "canonuser plugin without either client or server side");
244         return SASL_BADPROT;
245     }
246     
247     new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t));
248     if(!new_item) return SASL_NOMEM;
249
250     strncpy(new_item->name, plugname, PATH_MAX);
251
252     new_item->plug = plug;
253     new_item->next = canonuser_head;
254     canonuser_head = new_item;
255
256     return SASL_OK;
257 }
258
259 #ifdef MIN
260 #undef MIN
261 #endif
262 #define MIN(a,b) (((a) < (b))? (a):(b))
263
264 static int _canonuser_internal(const sasl_utils_t *utils,
265                                const char *user, unsigned ulen,
266                                unsigned flags __attribute__((unused)),
267                                char *out_user,
268                                unsigned out_umax, unsigned *out_ulen) 
269 {
270     unsigned i;
271     char *in_buf, *userin;
272     const char *begin_u;
273     size_t u_apprealm = 0;
274     sasl_server_conn_t *sconn = NULL;
275
276     if(!utils || !user) return SASL_BADPARAM;
277
278     in_buf = sasl_ALLOC((ulen + 2) * sizeof(char));
279     if(!in_buf) return SASL_NOMEM;
280
281     userin = in_buf;
282
283     memcpy(userin, user, ulen);
284     userin[ulen] = '\0';
285     
286     /* Strip User ID */
287     for(i=0;isspace((int)userin[i]) && i<ulen;i++);
288     begin_u = &(userin[i]);
289     if(i>0) ulen -= i;
290
291     for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--);
292     if(begin_u == &(userin[ulen])) {
293         sasl_FREE(in_buf);
294         utils->seterror(utils->conn, 0, "All-whitespace username.");
295         return SASL_FAIL;
296     }
297
298     if(utils->conn && utils->conn->type == SASL_CONN_SERVER)
299         sconn = (sasl_server_conn_t *)utils->conn;
300
301     /* Need to append realm if necessary (see sasl.h) */
302     if(sconn && sconn->user_realm && !strchr(user, '@')) {
303         u_apprealm = strlen(sconn->user_realm) + 1;
304     }
305     
306     /* Now Copy */
307     memcpy(out_user, begin_u, MIN(ulen, out_umax));
308     if(sconn && u_apprealm) {
309         if(ulen >= out_umax) return SASL_BUFOVER;
310         out_user[ulen] = '@';
311         memcpy(&(out_user[ulen+1]), sconn->user_realm,
312                MIN(u_apprealm-1, out_umax-ulen-1));
313     }
314     out_user[MIN(ulen + u_apprealm,out_umax)] = '\0';
315
316     if(ulen + u_apprealm > out_umax) return SASL_BUFOVER;
317
318     if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax);
319     
320     sasl_FREE(in_buf);
321     return SASL_OK;
322 }
323
324 static int _cu_internal_server(void *glob_context __attribute__((unused)),
325                                sasl_server_params_t *sparams,
326                                const char *user, unsigned ulen,
327                                unsigned flags,
328                                char *out_user,
329                                unsigned out_umax, unsigned *out_ulen) 
330 {
331     return _canonuser_internal(sparams->utils,
332                                user, ulen,
333                                flags, out_user, out_umax, out_ulen);
334 }
335
336 static int _cu_internal_client(void *glob_context __attribute__((unused)),
337                                sasl_client_params_t *cparams,
338                                const char *user, unsigned ulen,
339                                unsigned flags,
340                                char *out_user,
341                                unsigned out_umax, unsigned *out_ulen) 
342 {
343     return _canonuser_internal(cparams->utils,
344                                user, ulen,
345                                flags, out_user, out_umax, out_ulen);
346 }
347
348 static sasl_canonuser_plug_t canonuser_internal_plugin = {
349         0, /* features */
350         0, /* spare */
351         NULL, /* glob_context */
352         "INTERNAL", /* name */
353         NULL, /* canon_user_free */
354         _cu_internal_server,
355         _cu_internal_client,
356         NULL,
357         NULL,
358         NULL
359 };
360
361 int internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)),
362                             int max_version,
363                             int *out_version,
364                             sasl_canonuser_plug_t **plug,
365                             const char *plugname __attribute__((unused))) 
366 {
367     if(!out_version || !plug) return SASL_BADPARAM;
368
369     if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS;
370     
371     *out_version = SASL_CANONUSER_PLUG_VERSION;
372
373     *plug = &canonuser_internal_plugin;
374
375     return SASL_OK;
376 }