1 /* canonusr.c - user canonicalization support
2 * Rob Siemborski
3 */
4 /*
5 * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The name "Carnegie Mellon University" must not be used to
20 * endorse or promote products derived from this software without
21 * prior written permission. For permission or any other legal
22 * details, please contact
23 * Carnegie Mellon University
24 * Center for Technology Transfer and Enterprise Creation
25 * 4615 Forbes Avenue
26 * Suite 302
27 * Pittsburgh, PA 15213
28 * (412) 268-7393, fax: (412) 268-7395
29 * innovation@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 * (handled by INTERNAL plugin) */
67 /* a zero ulen or alen indicates that it is strlen(value) */
_sasl_canon_user(sasl_conn_t * conn,const char * user,unsigned ulen,unsigned flags,sasl_out_params_t * oparams)68 int _sasl_canon_user(sasl_conn_t *conn,
69 const char *user, unsigned ulen,
70 unsigned flags,
71 sasl_out_params_t *oparams)
72 {
73 canonuser_plug_list_t *ptr;
74 sasl_server_conn_t *sconn = NULL;
75 sasl_client_conn_t *cconn = NULL;
76 sasl_canon_user_t *cuser_cb;
77 sasl_getopt_t *getopt;
78 void *context;
79 int result;
80 const char *plugin_name = NULL;
81 char *user_buf;
82 unsigned *lenp;
83
84 if(!conn) return SASL_BADPARAM;
85 if(!user || !oparams) return SASL_BADPARAM;
86
87 if(flags & SASL_CU_AUTHID) {
88 user_buf = conn->authid_buf;
89 lenp = &(oparams->alen);
90 } else if (flags & SASL_CU_AUTHZID) {
91 user_buf = conn->user_buf;
92 lenp = &(oparams->ulen);
93 } else {
94 return SASL_BADPARAM;
95 }
96
97 if (conn->type == SASL_CONN_SERVER)
98 sconn = (sasl_server_conn_t *)conn;
99 else if (conn->type == SASL_CONN_CLIENT)
100 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,
107 SASL_CB_CANON_USER,
108 (sasl_callback_ft *)&cuser_cb,
109 &context);
110 if(result == SASL_OK && cuser_cb) {
111 result = cuser_cb(conn,
112 context,
113 user,
114 ulen,
115 flags,
116 (sconn ?
117 sconn->user_realm :
118 NULL),
119 user_buf,
120 CANON_BUF_SIZE,
121 lenp);
122
123
124 if (result != SASL_OK) return result;
125
126 /* Point the input copy at the stored buffer */
127 user = user_buf;
128 ulen = *lenp;
129 }
130
131 /* which plugin are we supposed to use? */
132 result = _sasl_getcallback(conn,
133 SASL_CB_GETOPT,
134 (sasl_callback_ft *)&getopt,
135 &context);
136 if (result == SASL_OK && getopt) {
137 getopt(context, NULL, "canon_user_plugin", &plugin_name, NULL);
138 }
139
140 if (!plugin_name) {
141 /* Use Default */
142 plugin_name = "INTERNAL";
143 }
144
145 for (ptr = canonuser_head; ptr; ptr = ptr->next) {
146 /* A match is if we match the internal name of the plugin, or if
147 * we match the filename (old-style) */
148 if ((ptr->plug->name && !strcmp(plugin_name, ptr->plug->name))
149 || !strcmp(plugin_name, ptr->name)) break;
150 }
151
152 /* We clearly don't have this one! */
153 if (!ptr) {
154 sasl_seterror(conn, 0, "desired canon_user plugin %s not found",
155 plugin_name);
156 return SASL_NOMECH;
157 }
158
159 if (sconn) {
160 /* we're a server */
161 result = ptr->plug->canon_user_server(ptr->plug->glob_context,
162 sconn->sparams,
163 user, ulen,
164 flags,
165 user_buf,
166 CANON_BUF_SIZE, lenp);
167 } else {
168 /* we're a client */
169 result = ptr->plug->canon_user_client(ptr->plug->glob_context,
170 cconn->cparams,
171 user, ulen,
172 flags,
173 user_buf,
174 CANON_BUF_SIZE, lenp);
175 }
176
177 if (result != SASL_OK) return result;
178
179 if ((flags & SASL_CU_AUTHID) && (flags & SASL_CU_AUTHZID)) {
180 /* We did both, so we need to copy the result into
181 * the buffer for the authzid from the buffer for the authid */
182 memcpy(conn->user_buf, conn->authid_buf, CANON_BUF_SIZE);
183 oparams->ulen = oparams->alen;
184 }
185
186 /* Set the appropriate oparams (lengths have already been set by lenp) */
187 if (flags & SASL_CU_AUTHID) {
188 oparams->authid = conn->authid_buf;
189 }
190
191 if (flags & SASL_CU_AUTHZID) {
192 oparams->user = conn->user_buf;
193 }
194
195 RETURN(conn, result);
196 }
197
198 /* Lookup all properties for authentication and/or authorization identity. */
_sasl_auxprop_lookup_user_props(sasl_conn_t * conn,unsigned flags,sasl_out_params_t * oparams)199 static int _sasl_auxprop_lookup_user_props (sasl_conn_t *conn,
200 unsigned flags,
201 sasl_out_params_t *oparams)
202 {
203 sasl_server_conn_t *sconn = NULL;
204 int result = SASL_OK;
205
206 if (!conn) return SASL_BADPARAM;
207 if (!oparams) return SASL_BADPARAM;
208
209 #ifndef macintosh
210 if (conn->type == SASL_CONN_SERVER) sconn = (sasl_server_conn_t *)conn;
211
212 /* do auxprop lookups (server only) */
213 if (sconn) {
214 int authz_result;
215 unsigned auxprop_lookup_flags = flags & SASL_CU_ASIS_MASK;
216
217 if (flags & SASL_CU_OVERRIDE) {
218 auxprop_lookup_flags |= SASL_AUXPROP_OVERRIDE;
219 }
220
221 if (flags & SASL_CU_AUTHID) {
222 result = _sasl_auxprop_lookup(sconn->sparams,
223 auxprop_lookup_flags,
224 oparams->authid,
225 oparams->alen);
226 } else {
227 result = SASL_CONTINUE;
228 }
229 if (flags & SASL_CU_AUTHZID) {
230 authz_result = _sasl_auxprop_lookup(sconn->sparams,
231 auxprop_lookup_flags | SASL_AUXPROP_AUTHZID,
232 oparams->user,
233 oparams->ulen);
234
235 if (result == SASL_CONTINUE) {
236 /* Only SASL_CU_AUTHZID was requested.
237 The authz_result value is authoritative. */
238 result = authz_result;
239 } else if (result == SASL_OK && authz_result != SASL_NOUSER) {
240 /* Use the authz_result value, unless "result"
241 already contains an error */
242 result = authz_result;
243 }
244 }
245
246 if ((flags & SASL_CU_EXTERNALLY_VERIFIED) && (result == SASL_NOUSER || result == SASL_NOMECH)) {
247 /* The called has explicitly told us that the authentication identity
248 was already verified or will be verified independently.
249 So a failure to retrieve any associated properties
250 is not an error. For example the caller is using Kerberos to verify user,
251 but the LDAPDB/SASLDB auxprop plugin doesn't contain any auxprops for
252 the user.
253 Another case is PLAIN/LOGIN not using auxprop to verify user passwords. */
254 result = SASL_OK;
255 }
256 }
257 #endif
258
259 RETURN(conn, result);
260 }
261
262 /* default behavior:
263 * Eliminate leading & trailing whitespace,
264 * null-terminate, and get into the outparams
265 * (handled by INTERNAL plugin).
266 *
267 * Server only: Also does auxprop lookups once username
268 * is canonicalized. */
_sasl_canon_user_lookup(sasl_conn_t * conn,const char * user,unsigned ulen,unsigned flags,sasl_out_params_t * oparams)269 int _sasl_canon_user_lookup (sasl_conn_t *conn,
270 const char *user,
271 unsigned ulen,
272 unsigned flags,
273 sasl_out_params_t *oparams)
274 {
275 int result;
276
277 result = _sasl_canon_user (conn,
278 user,
279 ulen,
280 flags,
281 oparams);
282 if (result == SASL_OK) {
283 result = _sasl_auxprop_lookup_user_props (conn,
284 flags,
285 oparams);
286 }
287
288 RETURN(conn, result);
289 }
290
_sasl_canonuser_free()291 void _sasl_canonuser_free()
292 {
293 canonuser_plug_list_t *ptr, *ptr_next;
294
295 for(ptr = canonuser_head; ptr; ptr = ptr_next) {
296 ptr_next = ptr->next;
297 if(ptr->plug->canon_user_free)
298 ptr->plug->canon_user_free(ptr->plug->glob_context,
299 sasl_global_utils);
300 sasl_FREE(ptr);
301 }
302
303 canonuser_head = NULL;
304 }
305
sasl_canonuser_add_plugin(const char * plugname,sasl_canonuser_init_t * canonuserfunc)306 int sasl_canonuser_add_plugin(const char *plugname,
307 sasl_canonuser_init_t *canonuserfunc)
308 {
309 int result, out_version;
310 canonuser_plug_list_t *new_item;
311 sasl_canonuser_plug_t *plug;
312
313 if(!plugname || strlen(plugname) > (PATH_MAX - 1)) {
314 sasl_seterror(NULL, 0,
315 "bad plugname passed to sasl_canonuser_add_plugin\n");
316 return SASL_BADPARAM;
317 }
318
319 result = canonuserfunc(sasl_global_utils, SASL_CANONUSER_PLUG_VERSION,
320 &out_version, &plug, plugname);
321
322 if(result != SASL_OK) {
323 _sasl_log(NULL, SASL_LOG_ERR, "%s_canonuser_plug_init() failed in sasl_canonuser_add_plugin(): %z\n",
324 plugname, result);
325 return result;
326 }
327
328 if(!plug->canon_user_server && !plug->canon_user_client) {
329 /* We need at least one of these implemented */
330 _sasl_log(NULL, SASL_LOG_ERR,
331 "canonuser plugin '%s' without either client or server side", plugname);
332 return SASL_BADPROT;
333 }
334
335 new_item = sasl_ALLOC(sizeof(canonuser_plug_list_t));
336 if(!new_item) return SASL_NOMEM;
337
338 strncpy(new_item->name, plugname, PATH_MAX - 1);
339 new_item->name[strlen(plugname)] = '\0';
340
341 new_item->plug = plug;
342 new_item->next = canonuser_head;
343 canonuser_head = new_item;
344
345 return SASL_OK;
346 }
347
348 #ifdef MIN
349 #undef MIN
350 #endif
351 #define MIN(a,b) (((a) < (b))? (a):(b))
352
_canonuser_internal(const sasl_utils_t * utils,const char * user,unsigned ulen,unsigned flags,char * out_user,unsigned out_umax,unsigned * out_ulen)353 static int _canonuser_internal(const sasl_utils_t *utils,
354 const char *user, unsigned ulen,
355 unsigned flags __attribute__((unused)),
356 char *out_user,
357 unsigned out_umax, unsigned *out_ulen)
358 {
359 unsigned i;
360 char *in_buf, *userin;
361 const char *begin_u;
362 unsigned u_apprealm = 0;
363 sasl_server_conn_t *sconn = NULL;
364
365 if(!utils || !user) return SASL_BADPARAM;
366
367 in_buf = sasl_ALLOC((ulen + 2) * sizeof(char));
368 if(!in_buf) return SASL_NOMEM;
369
370 userin = in_buf;
371
372 memcpy(userin, user, ulen);
373 userin[ulen] = '\0';
374
375 /* Strip User ID */
376 for(i=0;isspace((int)userin[i]) && i<ulen;i++);
377 begin_u = &(userin[i]);
378 if(i>0) ulen -= i;
379
380 for(;ulen > 0 && isspace((int)begin_u[ulen-1]); ulen--);
381 if(begin_u == &(userin[ulen])) {
382 sasl_FREE(in_buf);
383 utils->seterror(utils->conn, 0, "All-whitespace username.");
384 return SASL_FAIL;
385 }
386
387 if(utils->conn && utils->conn->type == SASL_CONN_SERVER)
388 sconn = (sasl_server_conn_t *)utils->conn;
389
390 /* Need to append realm if necessary (see sasl.h) */
391 if(sconn && sconn->user_realm && !strchr(user, '@')) {
392 u_apprealm = (unsigned) strlen(sconn->user_realm) + 1;
393 }
394
395 /* Now Copy */
396 memcpy(out_user, begin_u, MIN(ulen, out_umax));
397 if(sconn && u_apprealm) {
398 if(ulen >= out_umax) return SASL_BUFOVER;
399 out_user[ulen] = '@';
400 memcpy(&(out_user[ulen+1]), sconn->user_realm,
401 MIN(u_apprealm-1, out_umax-ulen-1));
402 }
403 out_user[MIN(ulen + u_apprealm,out_umax)] = '\0';
404
405 if(ulen + u_apprealm > out_umax) return SASL_BUFOVER;
406
407 if(out_ulen) *out_ulen = MIN(ulen + u_apprealm,out_umax);
408
409 sasl_FREE(in_buf);
410 return SASL_OK;
411 }
412
_cu_internal_server(void * glob_context,sasl_server_params_t * sparams,const char * user,unsigned ulen,unsigned flags,char * out_user,unsigned out_umax,unsigned * out_ulen)413 static int _cu_internal_server(void *glob_context __attribute__((unused)),
414 sasl_server_params_t *sparams,
415 const char *user, unsigned ulen,
416 unsigned flags,
417 char *out_user,
418 unsigned out_umax, unsigned *out_ulen)
419 {
420 return _canonuser_internal(sparams->utils,
421 user, ulen,
422 flags, out_user, out_umax, out_ulen);
423 }
424
_cu_internal_client(void * glob_context,sasl_client_params_t * cparams,const char * user,unsigned ulen,unsigned flags,char * out_user,unsigned out_umax,unsigned * out_ulen)425 static int _cu_internal_client(void *glob_context __attribute__((unused)),
426 sasl_client_params_t *cparams,
427 const char *user, unsigned ulen,
428 unsigned flags,
429 char *out_user,
430 unsigned out_umax, unsigned *out_ulen)
431 {
432 return _canonuser_internal(cparams->utils,
433 user, ulen,
434 flags, out_user, out_umax, out_ulen);
435 }
436
437 static sasl_canonuser_plug_t canonuser_internal_plugin = {
438 0, /* features */
439 0, /* spare */
440 NULL, /* glob_context */
441 "INTERNAL", /* name */
442 NULL, /* canon_user_free */
443 _cu_internal_server,
444 _cu_internal_client,
445 NULL,
446 NULL,
447 NULL
448 };
449
internal_canonuser_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_canonuser_plug_t ** plug,const char * plugname)450 int internal_canonuser_init(const sasl_utils_t *utils __attribute__((unused)),
451 int max_version,
452 int *out_version,
453 sasl_canonuser_plug_t **plug,
454 const char *plugname __attribute__((unused)))
455 {
456 if(!out_version || !plug) return SASL_BADPARAM;
457
458 if(max_version < SASL_CANONUSER_PLUG_VERSION) return SASL_BADVERS;
459
460 *out_version = SASL_CANONUSER_PLUG_VERSION;
461
462 *plug = &canonuser_internal_plugin;
463
464 return SASL_OK;
465 }
466