1 | /* SASL server API implementation |
---|
2 | * Rob Siemborski |
---|
3 | * Tim Martin |
---|
4 | * $Id: server.c,v 1.1.1.2 2003-02-12 22:33:36 ghudson Exp $ |
---|
5 | */ |
---|
6 | /* |
---|
7 | * Copyright (c) 2001 Carnegie Mellon University. All rights reserved. |
---|
8 | * |
---|
9 | * Redistribution and use in source and binary forms, with or without |
---|
10 | * modification, are permitted provided that the following conditions |
---|
11 | * are met: |
---|
12 | * |
---|
13 | * 1. Redistributions of source code must retain the above copyright |
---|
14 | * notice, this list of conditions and the following disclaimer. |
---|
15 | * |
---|
16 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
17 | * notice, this list of conditions and the following disclaimer in |
---|
18 | * the documentation and/or other materials provided with the |
---|
19 | * distribution. |
---|
20 | * |
---|
21 | * 3. The name "Carnegie Mellon University" must not be used to |
---|
22 | * endorse or promote products derived from this software without |
---|
23 | * prior written permission. For permission or any other legal |
---|
24 | * details, please contact |
---|
25 | * Office of Technology Transfer |
---|
26 | * Carnegie Mellon University |
---|
27 | * 5000 Forbes Avenue |
---|
28 | * Pittsburgh, PA 15213-3890 |
---|
29 | * (412) 268-4387, fax: (412) 268-7395 |
---|
30 | * tech-transfer@andrew.cmu.edu |
---|
31 | * |
---|
32 | * 4. Redistributions of any form whatsoever must retain the following |
---|
33 | * acknowledgment: |
---|
34 | * "This product includes software developed by Computing Services |
---|
35 | * at Carnegie Mellon University (http://www.cmu.edu/computing/)." |
---|
36 | * |
---|
37 | * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO |
---|
38 | * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
---|
39 | * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE |
---|
40 | * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
---|
41 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN |
---|
42 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
---|
43 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
---|
44 | */ |
---|
45 | |
---|
46 | /* local functions/structs don't start with sasl |
---|
47 | */ |
---|
48 | #include <config.h> |
---|
49 | #include <errno.h> |
---|
50 | #include <stdio.h> |
---|
51 | #include <stdlib.h> |
---|
52 | #include <limits.h> |
---|
53 | #ifndef macintosh |
---|
54 | #include <sys/types.h> |
---|
55 | #include <sys/stat.h> |
---|
56 | #endif |
---|
57 | #include <fcntl.h> |
---|
58 | #include <string.h> |
---|
59 | #include <ctype.h> |
---|
60 | |
---|
61 | #include "sasl.h" |
---|
62 | #include "saslint.h" |
---|
63 | #include "saslplug.h" |
---|
64 | #include "saslutil.h" |
---|
65 | |
---|
66 | #ifdef sun |
---|
67 | /* gotta define gethostname ourselves on suns */ |
---|
68 | extern int gethostname(char *, int); |
---|
69 | #endif |
---|
70 | |
---|
71 | #define DEFAULT_CHECKPASS_MECH "auxprop" |
---|
72 | |
---|
73 | /* Contains functions: |
---|
74 | * |
---|
75 | * sasl_server_init |
---|
76 | * sasl_server_new |
---|
77 | * sasl_listmech |
---|
78 | * sasl_server_start |
---|
79 | * sasl_server_step |
---|
80 | * sasl_checkpass |
---|
81 | * sasl_checkapop |
---|
82 | * sasl_user_exists |
---|
83 | * sasl_setpass |
---|
84 | */ |
---|
85 | |
---|
86 | /* if we've initialized the server sucessfully */ |
---|
87 | static int _sasl_server_active = 0; |
---|
88 | |
---|
89 | /* For access by other modules */ |
---|
90 | int _is_sasl_server_active(void) { return _sasl_server_active; } |
---|
91 | |
---|
92 | static int _sasl_checkpass(sasl_conn_t *conn, const char *service, |
---|
93 | const char *user, const char *pass); |
---|
94 | |
---|
95 | static mech_list_t *mechlist = NULL; /* global var which holds the list */ |
---|
96 | |
---|
97 | static sasl_global_callbacks_t global_callbacks; |
---|
98 | |
---|
99 | /* set the password for a user |
---|
100 | * conn -- SASL connection |
---|
101 | * user -- user name |
---|
102 | * pass -- plaintext password, may be NULL to remove user |
---|
103 | * passlen -- length of password, 0 = strlen(pass) |
---|
104 | * oldpass -- NULL will sometimes work |
---|
105 | * oldpasslen -- length of password, 0 = strlen(oldpass) |
---|
106 | * flags -- see flags below |
---|
107 | * |
---|
108 | * returns: |
---|
109 | * SASL_NOCHANGE -- proper entry already exists |
---|
110 | * SASL_NOMECH -- no authdb supports password setting as configured |
---|
111 | * SASL_NOVERIFY -- user exists, but no settable password present |
---|
112 | * SASL_DISABLED -- account disabled |
---|
113 | * SASL_PWLOCK -- password locked |
---|
114 | * SASL_WEAKPASS -- password too weak for security policy |
---|
115 | * SASL_NOUSERPASS -- user-supplied passwords not permitted |
---|
116 | * SASL_FAIL -- OS error |
---|
117 | * SASL_BADPARAM -- password too long |
---|
118 | * SASL_OK -- successful |
---|
119 | */ |
---|
120 | |
---|
121 | int sasl_setpass(sasl_conn_t *conn, |
---|
122 | const char *user, |
---|
123 | const char *pass, unsigned passlen, |
---|
124 | const char *oldpass, |
---|
125 | unsigned oldpasslen, |
---|
126 | unsigned flags) |
---|
127 | { |
---|
128 | int result=SASL_OK, tmpresult; |
---|
129 | sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; |
---|
130 | sasl_server_userdb_setpass_t *setpass_cb = NULL; |
---|
131 | void *context = NULL; |
---|
132 | mechanism_t *m; |
---|
133 | |
---|
134 | if (!_sasl_server_active || !mechlist) return SASL_NOTINIT; |
---|
135 | |
---|
136 | /* check params */ |
---|
137 | if (!conn) return SASL_BADPARAM; |
---|
138 | if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn); |
---|
139 | |
---|
140 | if ((!(flags & SASL_SET_DISABLE) && passlen == 0) |
---|
141 | || ((flags & SASL_SET_CREATE) && (flags & SASL_SET_DISABLE))) |
---|
142 | PARAMERROR(conn); |
---|
143 | |
---|
144 | /* call userdb callback function */ |
---|
145 | result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_SETPASS, |
---|
146 | &setpass_cb, &context); |
---|
147 | if(result == SASL_OK && setpass_cb) { |
---|
148 | tmpresult = setpass_cb(conn, context, user, pass, passlen, |
---|
149 | s_conn->sparams->propctx, flags); |
---|
150 | if(tmpresult != SASL_OK) { |
---|
151 | _sasl_log(conn, SASL_LOG_ERR, |
---|
152 | "setpass callback failed for %s: %z", |
---|
153 | user, tmpresult); |
---|
154 | } else { |
---|
155 | _sasl_log(conn, SASL_LOG_NOTE, |
---|
156 | "setpass callback succeeded for %s", user); |
---|
157 | } |
---|
158 | } else { |
---|
159 | result = SASL_OK; |
---|
160 | } |
---|
161 | |
---|
162 | /* now we let the mechanisms set their secrets */ |
---|
163 | for (m = mechlist->mech_list; m; m = m->next) { |
---|
164 | if (!m->plug->setpass) { |
---|
165 | /* can't set pass for this mech */ |
---|
166 | continue; |
---|
167 | } |
---|
168 | tmpresult = m->plug->setpass(m->plug->glob_context, |
---|
169 | ((sasl_server_conn_t *)conn)->sparams, |
---|
170 | user, |
---|
171 | pass, |
---|
172 | passlen, |
---|
173 | oldpass, oldpasslen, |
---|
174 | flags); |
---|
175 | if (tmpresult == SASL_OK) { |
---|
176 | _sasl_log(conn, SASL_LOG_NOTE, |
---|
177 | "%s: set secret for %s", m->plug->mech_name, user); |
---|
178 | |
---|
179 | m->condition = SASL_OK; /* if we previously thought the |
---|
180 | mechanism didn't have any user secrets |
---|
181 | we now think it does */ |
---|
182 | |
---|
183 | } else if (tmpresult == SASL_NOCHANGE) { |
---|
184 | _sasl_log(conn, SASL_LOG_NOTE, |
---|
185 | "%s: secret not changed for %s", m->plug->mech_name, user); |
---|
186 | } else { |
---|
187 | result = tmpresult; |
---|
188 | _sasl_log(conn, SASL_LOG_ERR, |
---|
189 | "%s: failed to set secret for %s: %z (%m)", |
---|
190 | m->plug->mech_name, user, tmpresult, |
---|
191 | #ifndef WIN32 |
---|
192 | errno |
---|
193 | #else |
---|
194 | GetLastError() |
---|
195 | #endif |
---|
196 | ); |
---|
197 | } |
---|
198 | } |
---|
199 | |
---|
200 | RETURN(conn, result); |
---|
201 | } |
---|
202 | |
---|
203 | /* local mechanism which disposes of server */ |
---|
204 | static void server_dispose(sasl_conn_t *pconn) |
---|
205 | { |
---|
206 | sasl_server_conn_t *s_conn= (sasl_server_conn_t *) pconn; |
---|
207 | context_list_t *cur, *cur_next; |
---|
208 | |
---|
209 | if (s_conn->mech |
---|
210 | && s_conn->mech->plug->mech_dispose) { |
---|
211 | s_conn->mech->plug->mech_dispose(pconn->context, |
---|
212 | s_conn->sparams->utils); |
---|
213 | } |
---|
214 | pconn->context = NULL; |
---|
215 | |
---|
216 | for(cur = s_conn->mech_contexts; cur; cur=cur_next) { |
---|
217 | cur_next = cur->next; |
---|
218 | if(cur->context) |
---|
219 | cur->mech->plug->mech_dispose(cur->context, s_conn->sparams->utils); |
---|
220 | sasl_FREE(cur); |
---|
221 | } |
---|
222 | s_conn->mech_contexts = NULL; |
---|
223 | |
---|
224 | _sasl_free_utils(&s_conn->sparams->utils); |
---|
225 | |
---|
226 | if (s_conn->sparams->propctx) |
---|
227 | prop_dispose(&s_conn->sparams->propctx); |
---|
228 | |
---|
229 | if (s_conn->user_realm) |
---|
230 | sasl_FREE(s_conn->user_realm); |
---|
231 | |
---|
232 | if (s_conn->sparams) |
---|
233 | sasl_FREE(s_conn->sparams); |
---|
234 | |
---|
235 | _sasl_conn_dispose(pconn); |
---|
236 | } |
---|
237 | |
---|
238 | static int init_mechlist(void) |
---|
239 | { |
---|
240 | sasl_utils_t *newutils = NULL; |
---|
241 | |
---|
242 | mechlist->mutex = sasl_MUTEX_ALLOC(); |
---|
243 | if(!mechlist->mutex) return SASL_FAIL; |
---|
244 | |
---|
245 | /* set util functions - need to do rest */ |
---|
246 | newutils = _sasl_alloc_utils(NULL, &global_callbacks); |
---|
247 | if (newutils == NULL) |
---|
248 | return SASL_NOMEM; |
---|
249 | |
---|
250 | newutils->checkpass = &sasl_checkpass; |
---|
251 | |
---|
252 | mechlist->utils = newutils; |
---|
253 | mechlist->mech_list=NULL; |
---|
254 | mechlist->mech_length=0; |
---|
255 | |
---|
256 | return SASL_OK; |
---|
257 | } |
---|
258 | |
---|
259 | /* |
---|
260 | * parameters: |
---|
261 | * p - entry point |
---|
262 | */ |
---|
263 | int sasl_server_add_plugin(const char *plugname, |
---|
264 | sasl_server_plug_init_t *p) |
---|
265 | { |
---|
266 | int plugcount; |
---|
267 | sasl_server_plug_t *pluglist; |
---|
268 | mechanism_t *mech; |
---|
269 | sasl_server_plug_init_t *entry_point; |
---|
270 | int result; |
---|
271 | int version; |
---|
272 | int lupe; |
---|
273 | |
---|
274 | if(!plugname || !p) return SASL_BADPARAM; |
---|
275 | |
---|
276 | entry_point = (sasl_server_plug_init_t *)p; |
---|
277 | |
---|
278 | /* call into the shared library asking for information about it */ |
---|
279 | /* version is filled in with the version of the plugin */ |
---|
280 | result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, &version, |
---|
281 | &pluglist, &plugcount); |
---|
282 | |
---|
283 | if ((result != SASL_OK) && (result != SASL_NOUSER)) { |
---|
284 | _sasl_log(NULL, SASL_LOG_DEBUG, |
---|
285 | "server add_plugin entry_point error %z\n", result); |
---|
286 | return result; |
---|
287 | } |
---|
288 | |
---|
289 | /* Make sure plugin is using the same SASL version as us */ |
---|
290 | if (version != SASL_SERVER_PLUG_VERSION) |
---|
291 | { |
---|
292 | _sasl_log(NULL, SASL_LOG_ERR, |
---|
293 | "version mismatch on plugin"); |
---|
294 | return SASL_BADVERS; |
---|
295 | } |
---|
296 | |
---|
297 | for (lupe=0;lupe < plugcount ;lupe++) |
---|
298 | { |
---|
299 | mech = sasl_ALLOC(sizeof(mechanism_t)); |
---|
300 | if (! mech) return SASL_NOMEM; |
---|
301 | |
---|
302 | mech->plug=pluglist++; |
---|
303 | if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) { |
---|
304 | sasl_FREE(mech); |
---|
305 | return SASL_NOMEM; |
---|
306 | } |
---|
307 | mech->version = version; |
---|
308 | |
---|
309 | /* wheather this mech actually has any users in it's db */ |
---|
310 | mech->condition = result; /* SASL_OK or SASL_NOUSER */ |
---|
311 | |
---|
312 | mech->next = mechlist->mech_list; |
---|
313 | mechlist->mech_list = mech; |
---|
314 | mechlist->mech_length++; |
---|
315 | } |
---|
316 | |
---|
317 | return SASL_OK; |
---|
318 | } |
---|
319 | |
---|
320 | static void server_done(void) { |
---|
321 | mechanism_t *m; |
---|
322 | mechanism_t *prevm; |
---|
323 | |
---|
324 | if (mechlist != NULL) |
---|
325 | { |
---|
326 | m=mechlist->mech_list; /* m point to beginning of the list */ |
---|
327 | |
---|
328 | while (m!=NULL) |
---|
329 | { |
---|
330 | prevm=m; |
---|
331 | m=m->next; |
---|
332 | |
---|
333 | if (prevm->plug->mech_free) { |
---|
334 | prevm->plug->mech_free(prevm->plug->glob_context, |
---|
335 | mechlist->utils); |
---|
336 | } |
---|
337 | |
---|
338 | sasl_FREE(prevm->plugname); |
---|
339 | sasl_FREE(prevm); |
---|
340 | } |
---|
341 | _sasl_free_utils(&mechlist->utils); |
---|
342 | sasl_MUTEX_FREE(mechlist->mutex); |
---|
343 | sasl_FREE(mechlist); |
---|
344 | mechlist = NULL; |
---|
345 | } |
---|
346 | |
---|
347 | /* Free the auxprop plugins */ |
---|
348 | _sasl_auxprop_free(); |
---|
349 | |
---|
350 | global_callbacks.callbacks = NULL; |
---|
351 | global_callbacks.appname = NULL; |
---|
352 | |
---|
353 | /* no longer active. fail on listmech's etc. */ |
---|
354 | _sasl_server_active = 0; |
---|
355 | } |
---|
356 | |
---|
357 | static int |
---|
358 | server_idle(sasl_conn_t *conn) |
---|
359 | { |
---|
360 | mechanism_t *m; |
---|
361 | if (! mechlist) |
---|
362 | return 0; |
---|
363 | |
---|
364 | for (m = mechlist->mech_list; |
---|
365 | m!=NULL; |
---|
366 | m = m->next) |
---|
367 | if (m->plug->idle |
---|
368 | && m->plug->idle(m->plug->glob_context, |
---|
369 | conn, |
---|
370 | conn ? ((sasl_server_conn_t *)conn)->sparams : NULL)) |
---|
371 | return 1; |
---|
372 | return 0; |
---|
373 | } |
---|
374 | |
---|
375 | static int load_config(const sasl_callback_t *verifyfile_cb) |
---|
376 | { |
---|
377 | int result; |
---|
378 | const char *path_to_config=NULL; |
---|
379 | char *c; |
---|
380 | char *config_filename=NULL; |
---|
381 | int len; |
---|
382 | const sasl_callback_t *getpath_cb=NULL; |
---|
383 | |
---|
384 | /* get the path to the plugins; for now the config file will reside there */ |
---|
385 | getpath_cb=_sasl_find_getpath_callback( global_callbacks.callbacks ); |
---|
386 | if (getpath_cb==NULL) return SASL_BADPARAM; |
---|
387 | |
---|
388 | /* getpath_cb->proc MUST be a sasl_getpath_t; if only c had a type |
---|
389 | system */ |
---|
390 | result = ((sasl_getpath_t *)(getpath_cb->proc))(getpath_cb->context, |
---|
391 | &path_to_config); |
---|
392 | if (result!=SASL_OK) goto done; |
---|
393 | if (path_to_config == NULL) path_to_config = ""; |
---|
394 | |
---|
395 | if ((c = strchr(path_to_config, PATHS_DELIMITER))) { |
---|
396 | *c = '\0'; |
---|
397 | } |
---|
398 | |
---|
399 | /* length = length of path + '/' + length of appname + ".conf" + 1 |
---|
400 | for '\0' */ |
---|
401 | len = strlen(path_to_config)+2+ strlen(global_callbacks.appname)+5+1; |
---|
402 | |
---|
403 | if (len > PATH_MAX ) { |
---|
404 | result = SASL_FAIL; |
---|
405 | goto done; |
---|
406 | } |
---|
407 | |
---|
408 | /* construct the filename for the config file */ |
---|
409 | config_filename = sasl_ALLOC(len); |
---|
410 | if (! config_filename) { |
---|
411 | result = SASL_NOMEM; |
---|
412 | goto done; |
---|
413 | } |
---|
414 | |
---|
415 | snprintf(config_filename, len, "%s/%s.conf", path_to_config, |
---|
416 | global_callbacks.appname); |
---|
417 | |
---|
418 | /* Ask the application if it's safe to use this file */ |
---|
419 | result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context, |
---|
420 | config_filename, SASL_VRFY_CONF); |
---|
421 | |
---|
422 | /* returns continue if this file is to be skipped */ |
---|
423 | |
---|
424 | /* returns SASL_CONTINUE if doesn't exist |
---|
425 | * if doesn't exist we can continue using default behavior |
---|
426 | */ |
---|
427 | if (result==SASL_OK) |
---|
428 | result=sasl_config_init(config_filename); |
---|
429 | |
---|
430 | done: |
---|
431 | if (config_filename) sasl_FREE(config_filename); |
---|
432 | |
---|
433 | return result; |
---|
434 | } |
---|
435 | |
---|
436 | /* |
---|
437 | * Verify that all the callbacks are valid |
---|
438 | */ |
---|
439 | static int verify_server_callbacks(const sasl_callback_t *callbacks) |
---|
440 | { |
---|
441 | if (callbacks == NULL) return SASL_OK; |
---|
442 | |
---|
443 | while (callbacks->id != SASL_CB_LIST_END) { |
---|
444 | if (callbacks->proc==NULL) return SASL_FAIL; |
---|
445 | |
---|
446 | callbacks++; |
---|
447 | } |
---|
448 | |
---|
449 | return SASL_OK; |
---|
450 | } |
---|
451 | |
---|
452 | static char *grab_field(char *line, char **eofield) |
---|
453 | { |
---|
454 | int d = 0; |
---|
455 | char *field; |
---|
456 | |
---|
457 | while (isspace((int) *line)) line++; |
---|
458 | |
---|
459 | /* find end of field */ |
---|
460 | while (line[d] && !isspace(((int) line[d]))) d++; |
---|
461 | field = sasl_ALLOC(d + 1); |
---|
462 | if (!field) { return NULL; } |
---|
463 | memcpy(field, line, d); |
---|
464 | field[d] = '\0'; |
---|
465 | *eofield = line + d; |
---|
466 | |
---|
467 | return field; |
---|
468 | } |
---|
469 | |
---|
470 | struct secflag_map_s { |
---|
471 | char *name; |
---|
472 | int value; |
---|
473 | }; |
---|
474 | |
---|
475 | struct secflag_map_s secflag_map[] = { |
---|
476 | { "noplaintext", SASL_SEC_NOPLAINTEXT }, |
---|
477 | { "noactive", SASL_SEC_NOACTIVE }, |
---|
478 | { "nodictionary", SASL_SEC_NODICTIONARY }, |
---|
479 | { "forward_secrecy", SASL_SEC_FORWARD_SECRECY }, |
---|
480 | { "noanonymous", SASL_SEC_NOANONYMOUS }, |
---|
481 | { "pass_credentials", SASL_SEC_PASS_CREDENTIALS }, |
---|
482 | { "mutual_auth", SASL_SEC_MUTUAL_AUTH }, |
---|
483 | { NULL, 0x0 } |
---|
484 | }; |
---|
485 | |
---|
486 | static int parse_mechlist_file(const char *mechlistfile) |
---|
487 | { |
---|
488 | FILE *f; |
---|
489 | char buf[1024]; |
---|
490 | char *t, *ptr; |
---|
491 | int r = 0; |
---|
492 | |
---|
493 | f = fopen(mechlistfile, "r"); |
---|
494 | if (!f) return SASL_FAIL; |
---|
495 | |
---|
496 | r = SASL_OK; |
---|
497 | while (fgets(buf, sizeof(buf), f) != NULL) { |
---|
498 | mechanism_t *n = sasl_ALLOC(sizeof(mechanism_t)); |
---|
499 | sasl_server_plug_t *nplug; |
---|
500 | |
---|
501 | if (n == NULL) { r = SASL_NOMEM; break; } |
---|
502 | n->version = SASL_SERVER_PLUG_VERSION; |
---|
503 | n->condition = SASL_CONTINUE; |
---|
504 | nplug = sasl_ALLOC(sizeof(sasl_server_plug_t)); |
---|
505 | if (nplug == NULL) { r = SASL_NOMEM; break; } |
---|
506 | memset(nplug, 0, sizeof(sasl_server_plug_t)); |
---|
507 | |
---|
508 | /* each line is: |
---|
509 | plugin-file WS mech_name WS max_ssf *(WS security_flag) RET |
---|
510 | */ |
---|
511 | |
---|
512 | /* grab file */ |
---|
513 | n->f = grab_field(buf, &ptr); |
---|
514 | |
---|
515 | /* grab mech_name */ |
---|
516 | nplug->mech_name = grab_field(ptr, &ptr); |
---|
517 | |
---|
518 | /* grab max_ssf */ |
---|
519 | nplug->max_ssf = strtol(ptr, &ptr, 10); |
---|
520 | |
---|
521 | /* grab security flags */ |
---|
522 | while (*ptr != '\n') { |
---|
523 | struct secflag_map_s *map; |
---|
524 | |
---|
525 | /* read security flag */ |
---|
526 | t = grab_field(ptr, &ptr); |
---|
527 | map = secflag_map; |
---|
528 | while (map->name) { |
---|
529 | if (!strcasecmp(t, map->name)) { |
---|
530 | nplug->security_flags |= map->value; |
---|
531 | break; |
---|
532 | } |
---|
533 | map++; |
---|
534 | } |
---|
535 | if (!map->name) { |
---|
536 | _sasl_log(NULL, SASL_LOG_ERR, |
---|
537 | "%s: couldn't identify flag '%s'", |
---|
538 | nplug->mech_name, t); |
---|
539 | } |
---|
540 | free(t); |
---|
541 | } |
---|
542 | |
---|
543 | /* insert mechanism into mechlist */ |
---|
544 | n->plug = nplug; |
---|
545 | n->next = mechlist->mech_list; |
---|
546 | mechlist->mech_list = n; |
---|
547 | mechlist->mech_length++; |
---|
548 | } |
---|
549 | |
---|
550 | fclose(f); |
---|
551 | return r; |
---|
552 | } |
---|
553 | |
---|
554 | /* initialize server drivers, done once per process |
---|
555 | * callbacks -- callbacks for all server connections; must include |
---|
556 | * getopt callback |
---|
557 | * appname -- name of calling application (for lower level logging) |
---|
558 | * results: |
---|
559 | * state -- server state |
---|
560 | * returns: |
---|
561 | * SASL_OK -- success |
---|
562 | * SASL_BADPARAM -- error in config file |
---|
563 | * SASL_NOMEM -- memory failure |
---|
564 | * SASL_BADVERS -- Mechanism version mismatch |
---|
565 | */ |
---|
566 | |
---|
567 | int sasl_server_init(const sasl_callback_t *callbacks, |
---|
568 | const char *appname) |
---|
569 | { |
---|
570 | int ret; |
---|
571 | const sasl_callback_t *vf; |
---|
572 | const char *pluginfile = NULL; |
---|
573 | #ifdef PIC |
---|
574 | sasl_getopt_t *getopt; |
---|
575 | void *context; |
---|
576 | #endif |
---|
577 | |
---|
578 | const add_plugin_list_t ep_list[] = { |
---|
579 | { "sasl_server_plug_init", (add_plugin_t *)sasl_server_add_plugin }, |
---|
580 | { "sasl_auxprop_plug_init", (add_plugin_t *)sasl_auxprop_add_plugin }, |
---|
581 | { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin }, |
---|
582 | { NULL, NULL } |
---|
583 | }; |
---|
584 | |
---|
585 | /* we require the appname to be non-null */ |
---|
586 | if (appname==NULL) return SASL_BADPARAM; |
---|
587 | |
---|
588 | ret = _sasl_common_init(); |
---|
589 | if (ret != SASL_OK) |
---|
590 | return ret; |
---|
591 | |
---|
592 | _sasl_server_cleanup_hook = &server_done; |
---|
593 | |
---|
594 | /* verify that the callbacks look ok */ |
---|
595 | ret = verify_server_callbacks(callbacks); |
---|
596 | if (ret != SASL_OK) return ret; |
---|
597 | |
---|
598 | global_callbacks.callbacks = callbacks; |
---|
599 | global_callbacks.appname = appname; |
---|
600 | |
---|
601 | /* allocate mechlist and set it to empty */ |
---|
602 | mechlist = sasl_ALLOC(sizeof(mech_list_t)); |
---|
603 | if (mechlist == NULL) return SASL_NOMEM; |
---|
604 | |
---|
605 | ret = init_mechlist(); |
---|
606 | if (ret != SASL_OK) return ret; |
---|
607 | |
---|
608 | vf = _sasl_find_verifyfile_callback(callbacks); |
---|
609 | |
---|
610 | /* load config file if applicable */ |
---|
611 | ret = load_config(vf); |
---|
612 | if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) { |
---|
613 | return ret; |
---|
614 | } |
---|
615 | |
---|
616 | /* load internal plugins */ |
---|
617 | sasl_server_add_plugin("EXTERNAL", &external_server_plug_init); |
---|
618 | |
---|
619 | #ifdef PIC |
---|
620 | /* delayed loading of plugins? (DSO only, as it doesn't |
---|
621 | * make much [any] sense to delay in the static library case) */ |
---|
622 | if (_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context) |
---|
623 | == SASL_OK) { |
---|
624 | /* No sasl_conn_t was given to getcallback, so we provide the |
---|
625 | * global callbacks structure */ |
---|
626 | ret = getopt(&global_callbacks, NULL, "plugin_list", &pluginfile, NULL); |
---|
627 | } |
---|
628 | #endif |
---|
629 | |
---|
630 | if (pluginfile != NULL) { |
---|
631 | /* this file should contain a list of plugins available. |
---|
632 | we'll load on demand. */ |
---|
633 | |
---|
634 | /* Ask the application if it's safe to use this file */ |
---|
635 | ret = ((sasl_verifyfile_t *)(vf->proc))(vf->context, |
---|
636 | pluginfile, |
---|
637 | SASL_VRFY_CONF); |
---|
638 | if (ret != SASL_OK) { |
---|
639 | _sasl_log(NULL, SASL_LOG_ERR, |
---|
640 | "unable to load plugin list %s: %z", pluginfile, ret); |
---|
641 | } |
---|
642 | |
---|
643 | if (ret == SASL_OK) { |
---|
644 | ret = parse_mechlist_file(pluginfile); |
---|
645 | } |
---|
646 | } else { |
---|
647 | /* load all plugins now */ |
---|
648 | ret = _sasl_load_plugins(ep_list, |
---|
649 | _sasl_find_getpath_callback(callbacks), |
---|
650 | _sasl_find_verifyfile_callback(callbacks)); |
---|
651 | } |
---|
652 | |
---|
653 | if (ret == SASL_OK) { |
---|
654 | /* _sasl_server_active shows if we're active or not. |
---|
655 | server_done() sets it back to 0 */ |
---|
656 | _sasl_server_active = 1; |
---|
657 | _sasl_server_idle_hook = &server_idle; |
---|
658 | |
---|
659 | ret = _sasl_build_mechlist(); |
---|
660 | } |
---|
661 | |
---|
662 | return ret; |
---|
663 | } |
---|
664 | |
---|
665 | /* |
---|
666 | * Once we have the users plaintext password we |
---|
667 | * may want to transition them. That is put entries |
---|
668 | * for them in the passwd database for other |
---|
669 | * stronger mechanism |
---|
670 | * |
---|
671 | * for example PLAIN -> CRAM-MD5 |
---|
672 | */ |
---|
673 | static int |
---|
674 | _sasl_transition(sasl_conn_t * conn, |
---|
675 | const char * pass, |
---|
676 | unsigned passlen) |
---|
677 | { |
---|
678 | const char *dotrans = "n"; |
---|
679 | sasl_getopt_t *getopt; |
---|
680 | int result = SASL_OK; |
---|
681 | void *context; |
---|
682 | |
---|
683 | if (! conn) |
---|
684 | return SASL_BADPARAM; |
---|
685 | |
---|
686 | if (! conn->oparams.authid) |
---|
687 | PARAMERROR(conn); |
---|
688 | |
---|
689 | /* check if this is enabled: default to false */ |
---|
690 | if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) |
---|
691 | { |
---|
692 | getopt(context, NULL, "auto_transition", &dotrans, NULL); |
---|
693 | if (dotrans == NULL) dotrans = "n"; |
---|
694 | } |
---|
695 | |
---|
696 | if (*dotrans == '1' || *dotrans == 'y' || |
---|
697 | (*dotrans == 'o' && dotrans[1] == 'n') || *dotrans == 't') { |
---|
698 | /* ok, it's on! */ |
---|
699 | result = sasl_setpass(conn, |
---|
700 | conn->oparams.authid, |
---|
701 | pass, |
---|
702 | passlen, |
---|
703 | NULL, 0, 0); |
---|
704 | } |
---|
705 | |
---|
706 | RETURN(conn,result); |
---|
707 | } |
---|
708 | |
---|
709 | |
---|
710 | /* create context for a single SASL connection |
---|
711 | * service -- registered name of the service using SASL (e.g. "imap") |
---|
712 | * serverFQDN -- Fully qualified domain name of server. NULL means use |
---|
713 | * gethostname() or equivalent. |
---|
714 | * Useful for multi-homed servers. |
---|
715 | * user_realm -- permits multiple user realms on server, NULL = default |
---|
716 | * iplocalport -- server IPv4/IPv6 domain literal string with port |
---|
717 | * (if NULL, then mechanisms requiring IPaddr are disabled) |
---|
718 | * ipremoteport -- client IPv4/IPv6 domain literal string with port |
---|
719 | * (if NULL, then mechanisms requiring IPaddr are disabled) |
---|
720 | * callbacks -- callbacks (e.g., authorization, lang, new getopt context) |
---|
721 | * flags -- usage flags (see above) |
---|
722 | * returns: |
---|
723 | * pconn -- new connection context |
---|
724 | * |
---|
725 | * returns: |
---|
726 | * SASL_OK -- success |
---|
727 | * SASL_NOMEM -- not enough memory |
---|
728 | */ |
---|
729 | |
---|
730 | int sasl_server_new(const char *service, |
---|
731 | const char *serverFQDN, |
---|
732 | const char *user_realm, |
---|
733 | const char *iplocalport, |
---|
734 | const char *ipremoteport, |
---|
735 | const sasl_callback_t *callbacks, |
---|
736 | unsigned flags, |
---|
737 | sasl_conn_t **pconn) |
---|
738 | { |
---|
739 | int result; |
---|
740 | sasl_server_conn_t *serverconn; |
---|
741 | sasl_utils_t *utils; |
---|
742 | |
---|
743 | if (_sasl_server_active==0) return SASL_NOTINIT; |
---|
744 | if (! pconn) return SASL_FAIL; |
---|
745 | if (! service) return SASL_FAIL; |
---|
746 | |
---|
747 | *pconn=sasl_ALLOC(sizeof(sasl_server_conn_t)); |
---|
748 | if (*pconn==NULL) return SASL_NOMEM; |
---|
749 | |
---|
750 | memset(*pconn, 0, sizeof(sasl_server_conn_t)); |
---|
751 | |
---|
752 | serverconn = (sasl_server_conn_t *)*pconn; |
---|
753 | |
---|
754 | /* make sparams */ |
---|
755 | serverconn->sparams=sasl_ALLOC(sizeof(sasl_server_params_t)); |
---|
756 | if (serverconn->sparams==NULL) |
---|
757 | MEMERROR(*pconn); |
---|
758 | |
---|
759 | memset(serverconn->sparams, 0, sizeof(sasl_server_params_t)); |
---|
760 | |
---|
761 | (*pconn)->destroy_conn = &server_dispose; |
---|
762 | result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_SERVER, |
---|
763 | &server_idle, serverFQDN, |
---|
764 | iplocalport, ipremoteport, |
---|
765 | callbacks, &global_callbacks); |
---|
766 | if (result != SASL_OK) |
---|
767 | goto done_error; |
---|
768 | |
---|
769 | |
---|
770 | /* set util functions - need to do rest */ |
---|
771 | utils=_sasl_alloc_utils(*pconn, &global_callbacks); |
---|
772 | if (!utils) { |
---|
773 | result = SASL_NOMEM; |
---|
774 | goto done_error; |
---|
775 | } |
---|
776 | |
---|
777 | utils->checkpass = &sasl_checkpass; |
---|
778 | |
---|
779 | /* Setup the propctx -> We'll assume the default size */ |
---|
780 | serverconn->sparams->propctx=prop_new(0); |
---|
781 | if(!serverconn->sparams->propctx) { |
---|
782 | result = SASL_NOMEM; |
---|
783 | goto done_error; |
---|
784 | } |
---|
785 | |
---|
786 | serverconn->sparams->service = (*pconn)->service; |
---|
787 | serverconn->sparams->servicelen = strlen((*pconn)->service); |
---|
788 | |
---|
789 | serverconn->sparams->appname = global_callbacks.appname; |
---|
790 | serverconn->sparams->applen = strlen(global_callbacks.appname); |
---|
791 | |
---|
792 | serverconn->sparams->serverFQDN = (*pconn)->serverFQDN; |
---|
793 | serverconn->sparams->slen = strlen((*pconn)->serverFQDN); |
---|
794 | |
---|
795 | if (user_realm) { |
---|
796 | result = _sasl_strdup(user_realm, &serverconn->user_realm, NULL); |
---|
797 | serverconn->sparams->urlen = strlen(user_realm); |
---|
798 | serverconn->sparams->user_realm = serverconn->user_realm; |
---|
799 | } else { |
---|
800 | serverconn->user_realm = NULL; |
---|
801 | /* the sparams is already zeroed */ |
---|
802 | } |
---|
803 | |
---|
804 | serverconn->sparams->utils = utils; |
---|
805 | serverconn->sparams->transition = &_sasl_transition; |
---|
806 | serverconn->sparams->canon_user = &_sasl_canon_user; |
---|
807 | serverconn->sparams->props = serverconn->base.props; |
---|
808 | serverconn->sparams->flags = flags; |
---|
809 | |
---|
810 | if(result == SASL_OK) return SASL_OK; |
---|
811 | |
---|
812 | done_error: |
---|
813 | _sasl_conn_dispose(*pconn); |
---|
814 | sasl_FREE(*pconn); |
---|
815 | *pconn = NULL; |
---|
816 | return result; |
---|
817 | } |
---|
818 | |
---|
819 | /* |
---|
820 | * The rule is: |
---|
821 | * IF mech strength + external strength < min ssf THEN FAIL |
---|
822 | * We also have to look at the security properties and make sure |
---|
823 | * that this mechanism has everything we want |
---|
824 | */ |
---|
825 | static int mech_permitted(sasl_conn_t *conn, |
---|
826 | mechanism_t *mech) |
---|
827 | { |
---|
828 | sasl_server_conn_t *s_conn = (sasl_server_conn_t *)conn; |
---|
829 | const sasl_server_plug_t *plug; |
---|
830 | int myflags; |
---|
831 | context_list_t *cur; |
---|
832 | sasl_getopt_t *getopt; |
---|
833 | void *context; |
---|
834 | sasl_ssf_t minssf = 0; |
---|
835 | |
---|
836 | if(!conn) return 0; |
---|
837 | |
---|
838 | if(! mech || ! mech->plug) { |
---|
839 | PARAMERROR(conn); |
---|
840 | return 0; |
---|
841 | } |
---|
842 | |
---|
843 | plug = mech->plug; |
---|
844 | |
---|
845 | /* get the list of allowed mechanisms (default = all) */ |
---|
846 | if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) |
---|
847 | == SASL_OK) { |
---|
848 | const char *mlist; |
---|
849 | |
---|
850 | getopt(context, NULL, "mech_list", &mlist, NULL); |
---|
851 | |
---|
852 | /* if we have a list, check the plugin against it */ |
---|
853 | if (mlist) { |
---|
854 | const char *cp; |
---|
855 | |
---|
856 | while (*mlist) { |
---|
857 | for (cp = mlist; *cp && !isspace((int) *cp); cp++); |
---|
858 | if (((size_t) (cp - mlist) == strlen(plug->mech_name)) && |
---|
859 | !strncasecmp(mlist, plug->mech_name, |
---|
860 | strlen(plug->mech_name))) { |
---|
861 | break; |
---|
862 | } |
---|
863 | mlist = cp; |
---|
864 | while (*mlist && isspace((int) *mlist)) mlist++; |
---|
865 | } |
---|
866 | |
---|
867 | if (!*mlist) return 0; /* reached EOS -> not in our list */ |
---|
868 | } |
---|
869 | } |
---|
870 | |
---|
871 | /* setup parameters for the call to mech_avail */ |
---|
872 | s_conn->sparams->serverFQDN=conn->serverFQDN; |
---|
873 | s_conn->sparams->service=conn->service; |
---|
874 | s_conn->sparams->user_realm=s_conn->user_realm; |
---|
875 | s_conn->sparams->props=conn->props; |
---|
876 | s_conn->sparams->external_ssf=conn->external.ssf; |
---|
877 | |
---|
878 | /* Check if we have banished this one already */ |
---|
879 | for(cur = s_conn->mech_contexts; cur; cur=cur->next) { |
---|
880 | if(cur->mech == mech) { |
---|
881 | /* If it's not mech_avail'd, then stop now */ |
---|
882 | if(!cur->context) return 0; |
---|
883 | break; |
---|
884 | } |
---|
885 | } |
---|
886 | |
---|
887 | if (!strcasecmp(plug->mech_name, "EXTERNAL")) { |
---|
888 | /* Special case for the external mechanism */ |
---|
889 | if (conn->props.min_ssf > conn->external.ssf |
---|
890 | || ! conn->external.auth_id) { |
---|
891 | sasl_seterror(conn, SASL_NOLOG, |
---|
892 | "External SSF not good enough"); |
---|
893 | return 0; |
---|
894 | } |
---|
895 | } else { |
---|
896 | if (conn->props.min_ssf < conn->external.ssf) { |
---|
897 | minssf = 0; |
---|
898 | } else { |
---|
899 | minssf = conn->props.min_ssf - conn->external.ssf; |
---|
900 | } |
---|
901 | |
---|
902 | /* Generic mechanism */ |
---|
903 | if (plug->max_ssf < minssf) { |
---|
904 | sasl_seterror(conn, SASL_NOLOG, |
---|
905 | "mech %s is too weak", plug->mech_name); |
---|
906 | return 0; /* too weak */ |
---|
907 | } |
---|
908 | |
---|
909 | } |
---|
910 | |
---|
911 | context = NULL; |
---|
912 | if(plug->mech_avail |
---|
913 | && plug->mech_avail(plug->glob_context, |
---|
914 | s_conn->sparams, (void **)&context) != SASL_OK ) { |
---|
915 | /* Mark this mech as no good for this connection */ |
---|
916 | cur = sasl_ALLOC(sizeof(context_list_t)); |
---|
917 | if(!cur) { |
---|
918 | MEMERROR(conn); |
---|
919 | return 0; |
---|
920 | } |
---|
921 | cur->context = NULL; |
---|
922 | cur->mech = mech; |
---|
923 | cur->next = s_conn->mech_contexts; |
---|
924 | s_conn->mech_contexts = cur; |
---|
925 | |
---|
926 | /* Error should be set by mech_avail call */ |
---|
927 | return 0; |
---|
928 | } else if(context) { |
---|
929 | /* Save this context */ |
---|
930 | cur = sasl_ALLOC(sizeof(context_list_t)); |
---|
931 | if(!cur) { |
---|
932 | MEMERROR(conn); |
---|
933 | return 0; |
---|
934 | } |
---|
935 | cur->context = context; |
---|
936 | cur->mech = mech; |
---|
937 | cur->next = s_conn->mech_contexts; |
---|
938 | s_conn->mech_contexts = cur; |
---|
939 | } |
---|
940 | |
---|
941 | /* Generic mechanism */ |
---|
942 | if (plug->max_ssf < minssf) { |
---|
943 | sasl_seterror(conn, SASL_NOLOG, "too weak"); |
---|
944 | return 0; /* too weak */ |
---|
945 | } |
---|
946 | |
---|
947 | /* if there are no users in the secrets database we can't use this |
---|
948 | mechanism */ |
---|
949 | if (mech->condition == SASL_NOUSER) { |
---|
950 | sasl_seterror(conn, 0, "no users in secrets db"); |
---|
951 | return 0; |
---|
952 | } |
---|
953 | |
---|
954 | /* Can it meet our features? */ |
---|
955 | if ((conn->flags & SASL_NEED_PROXY) && |
---|
956 | !(plug->features & SASL_FEAT_ALLOWS_PROXY)) { |
---|
957 | return 0; |
---|
958 | } |
---|
959 | |
---|
960 | /* security properties---if there are any flags that differ and are |
---|
961 | in what the connection are requesting, then fail */ |
---|
962 | |
---|
963 | /* special case plaintext */ |
---|
964 | myflags = conn->props.security_flags; |
---|
965 | |
---|
966 | /* if there's an external layer this is no longer plaintext */ |
---|
967 | if ((conn->props.min_ssf <= conn->external.ssf) && |
---|
968 | (conn->external.ssf > 1)) { |
---|
969 | myflags &= ~SASL_SEC_NOPLAINTEXT; |
---|
970 | } |
---|
971 | |
---|
972 | /* do we want to special case SASL_SEC_PASS_CREDENTIALS? nah.. */ |
---|
973 | if (((myflags ^ plug->security_flags) & myflags) != 0) { |
---|
974 | sasl_seterror(conn, SASL_NOLOG, |
---|
975 | "security flags do not match required"); |
---|
976 | return 0; |
---|
977 | } |
---|
978 | |
---|
979 | /* Check Features */ |
---|
980 | if(plug->features & SASL_FEAT_GETSECRET) { |
---|
981 | /* We no longer support sasl_server_{get,put}secret */ |
---|
982 | sasl_seterror(conn, 0, |
---|
983 | "mech %s requires unprovided secret facility", |
---|
984 | plug->mech_name); |
---|
985 | return 0; |
---|
986 | } |
---|
987 | |
---|
988 | return 1; |
---|
989 | } |
---|
990 | |
---|
991 | /* |
---|
992 | * make the authorization |
---|
993 | * |
---|
994 | */ |
---|
995 | |
---|
996 | static int do_authorization(sasl_server_conn_t *s_conn) |
---|
997 | { |
---|
998 | int ret; |
---|
999 | sasl_authorize_t *authproc; |
---|
1000 | void *auth_context; |
---|
1001 | |
---|
1002 | /* now let's see if authname is allowed to proxy for username! */ |
---|
1003 | |
---|
1004 | /* check the proxy callback */ |
---|
1005 | if (_sasl_getcallback(&s_conn->base, SASL_CB_PROXY_POLICY, |
---|
1006 | &authproc, &auth_context) != SASL_OK) { |
---|
1007 | INTERROR(&s_conn->base, SASL_NOAUTHZ); |
---|
1008 | } |
---|
1009 | |
---|
1010 | ret = authproc(&(s_conn->base), auth_context, |
---|
1011 | s_conn->base.oparams.user, s_conn->base.oparams.ulen, |
---|
1012 | s_conn->base.oparams.authid, s_conn->base.oparams.alen, |
---|
1013 | s_conn->user_realm, |
---|
1014 | (s_conn->user_realm ? strlen(s_conn->user_realm) : 0), |
---|
1015 | s_conn->sparams->propctx); |
---|
1016 | |
---|
1017 | RETURN(&s_conn->base, ret); |
---|
1018 | } |
---|
1019 | |
---|
1020 | |
---|
1021 | /* start a mechanism exchange within a connection context |
---|
1022 | * mech -- the mechanism name client requested |
---|
1023 | * clientin -- client initial response (NUL terminated), NULL if empty |
---|
1024 | * clientinlen -- length of initial response |
---|
1025 | * serverout -- initial server challenge, NULL if done |
---|
1026 | * (library handles freeing this string) |
---|
1027 | * serveroutlen -- length of initial server challenge |
---|
1028 | * output: |
---|
1029 | * pconn -- the connection negotiation state on success |
---|
1030 | * |
---|
1031 | * Same returns as sasl_server_step() or |
---|
1032 | * SASL_NOMECH if mechanism not available. |
---|
1033 | */ |
---|
1034 | int sasl_server_start(sasl_conn_t *conn, |
---|
1035 | const char *mech, |
---|
1036 | const char *clientin, |
---|
1037 | unsigned clientinlen, |
---|
1038 | const char **serverout, |
---|
1039 | unsigned *serveroutlen) |
---|
1040 | { |
---|
1041 | sasl_server_conn_t *s_conn=(sasl_server_conn_t *) conn; |
---|
1042 | int result; |
---|
1043 | context_list_t *cur, **prev; |
---|
1044 | mechanism_t *m; |
---|
1045 | |
---|
1046 | if (_sasl_server_active==0) return SASL_NOTINIT; |
---|
1047 | |
---|
1048 | /* make sure mech is valid mechanism |
---|
1049 | if not return appropriate error */ |
---|
1050 | m=mechlist->mech_list; |
---|
1051 | |
---|
1052 | /* check parameters */ |
---|
1053 | if(!conn) return SASL_BADPARAM; |
---|
1054 | |
---|
1055 | if (!mech || ((clientin==NULL) && (clientinlen>0))) |
---|
1056 | PARAMERROR(conn); |
---|
1057 | |
---|
1058 | if(serverout) *serverout = NULL; |
---|
1059 | if(serveroutlen) *serveroutlen = 0; |
---|
1060 | |
---|
1061 | while (m!=NULL) |
---|
1062 | { |
---|
1063 | if ( strcasecmp(mech,m->plug->mech_name)==0) |
---|
1064 | { |
---|
1065 | break; |
---|
1066 | } |
---|
1067 | m=m->next; |
---|
1068 | } |
---|
1069 | |
---|
1070 | if (m==NULL) { |
---|
1071 | sasl_seterror(conn, 0, "Couldn't find mech %s", mech); |
---|
1072 | result = SASL_NOMECH; |
---|
1073 | goto done; |
---|
1074 | } |
---|
1075 | |
---|
1076 | /* Make sure that we're willing to use this mech */ |
---|
1077 | if (! mech_permitted(conn, m)) { |
---|
1078 | result = SASL_NOMECH; |
---|
1079 | goto done; |
---|
1080 | } |
---|
1081 | |
---|
1082 | if (m->condition == SASL_CONTINUE) { |
---|
1083 | sasl_server_plug_init_t *entry_point; |
---|
1084 | void *library = NULL; |
---|
1085 | sasl_server_plug_t *pluglist; |
---|
1086 | int version, plugcount; |
---|
1087 | int l = 0; |
---|
1088 | |
---|
1089 | /* need to load this plugin */ |
---|
1090 | result = _sasl_get_plugin(m->f, |
---|
1091 | _sasl_find_verifyfile_callback(global_callbacks.callbacks), |
---|
1092 | &library); |
---|
1093 | |
---|
1094 | if (result == SASL_OK) { |
---|
1095 | result = _sasl_locate_entry(library, "sasl_server_plug_init", |
---|
1096 | (void **)&entry_point); |
---|
1097 | } |
---|
1098 | |
---|
1099 | if (result == SASL_OK) { |
---|
1100 | result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, |
---|
1101 | &version, &pluglist, &plugcount); |
---|
1102 | } |
---|
1103 | |
---|
1104 | if (result == SASL_OK) { |
---|
1105 | /* find the correct mechanism in this plugin */ |
---|
1106 | for (l = 0; l < plugcount; l++) { |
---|
1107 | if (!strcasecmp(pluglist[l].mech_name, |
---|
1108 | m->plug->mech_name)) break; |
---|
1109 | } |
---|
1110 | if (l == plugcount) { |
---|
1111 | result = SASL_NOMECH; |
---|
1112 | } |
---|
1113 | } |
---|
1114 | if (result == SASL_OK) { |
---|
1115 | /* check that the parameters are the same */ |
---|
1116 | if ((pluglist[l].max_ssf != m->plug->max_ssf) || |
---|
1117 | (pluglist[l].security_flags != m->plug->security_flags)) { |
---|
1118 | _sasl_log(conn, SASL_LOG_ERR, |
---|
1119 | "%s: security parameters don't match mechlist file", |
---|
1120 | pluglist[l].mech_name); |
---|
1121 | result = SASL_NOMECH; |
---|
1122 | } |
---|
1123 | } |
---|
1124 | if (result == SASL_OK) { |
---|
1125 | /* copy mechlist over */ |
---|
1126 | sasl_FREE((sasl_server_plug_t *) m->plug); |
---|
1127 | m->plug = &pluglist[l]; |
---|
1128 | m->condition = SASL_OK; |
---|
1129 | } |
---|
1130 | |
---|
1131 | if (result != SASL_OK) { |
---|
1132 | /* The library will eventually be freed, don't sweat it */ |
---|
1133 | RETURN(conn, result); |
---|
1134 | } |
---|
1135 | } |
---|
1136 | |
---|
1137 | /* We used to setup sparams HERE, but now it's done |
---|
1138 | inside of mech_permitted (which is called above) */ |
---|
1139 | prev = &s_conn->mech_contexts; |
---|
1140 | for(cur = *prev; cur; prev=&cur->next,cur=cur->next) { |
---|
1141 | if(cur->mech == m) { |
---|
1142 | if(!cur->context) { |
---|
1143 | sasl_seterror(conn, 0, |
---|
1144 | "Got past mech_permitted with a disallowed mech!"); |
---|
1145 | return SASL_NOMECH; |
---|
1146 | } |
---|
1147 | /* If we find it, we need to pull cur out of the |
---|
1148 | list so it won't be freed later! */ |
---|
1149 | (*prev)->next = cur->next; |
---|
1150 | conn->context = cur->context; |
---|
1151 | sasl_FREE(cur); |
---|
1152 | } |
---|
1153 | } |
---|
1154 | |
---|
1155 | s_conn->mech = m; |
---|
1156 | |
---|
1157 | if(!conn->context) { |
---|
1158 | /* Note that we don't hand over a new challenge */ |
---|
1159 | result = s_conn->mech->plug->mech_new(s_conn->mech->plug->glob_context, |
---|
1160 | s_conn->sparams, |
---|
1161 | NULL, |
---|
1162 | 0, |
---|
1163 | &(conn->context)); |
---|
1164 | } else { |
---|
1165 | /* the work was already done by mech_avail! */ |
---|
1166 | result = SASL_OK; |
---|
1167 | } |
---|
1168 | |
---|
1169 | if (result == SASL_OK) { |
---|
1170 | if(clientin) { |
---|
1171 | if(s_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) { |
---|
1172 | /* Remote sent first, but mechanism does not support it. |
---|
1173 | * RFC 2222 says we fail at this point. */ |
---|
1174 | sasl_seterror(conn, 0, |
---|
1175 | "Remote sent first but mech does not allow it."); |
---|
1176 | result = SASL_BADPROT; |
---|
1177 | } else { |
---|
1178 | /* Mech wants client-first, so let them have it */ |
---|
1179 | result = sasl_server_step(conn, |
---|
1180 | clientin, clientinlen, |
---|
1181 | serverout, serveroutlen); |
---|
1182 | } |
---|
1183 | } else { |
---|
1184 | if(s_conn->mech->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) { |
---|
1185 | /* Mech wants client first anyway, so we should do that */ |
---|
1186 | *serverout = ""; |
---|
1187 | *serveroutlen = 0; |
---|
1188 | result = SASL_CONTINUE; |
---|
1189 | } else { |
---|
1190 | /* Mech wants server-first, so let them have it */ |
---|
1191 | result = sasl_server_step(conn, |
---|
1192 | clientin, clientinlen, |
---|
1193 | serverout, serveroutlen); |
---|
1194 | } |
---|
1195 | } |
---|
1196 | } |
---|
1197 | |
---|
1198 | done: |
---|
1199 | if( result != SASL_OK |
---|
1200 | && result != SASL_CONTINUE |
---|
1201 | && result != SASL_INTERACT) { |
---|
1202 | if(conn->context) { |
---|
1203 | s_conn->mech->plug->mech_dispose(conn->context, |
---|
1204 | s_conn->sparams->utils); |
---|
1205 | conn->context = NULL; |
---|
1206 | } |
---|
1207 | } |
---|
1208 | |
---|
1209 | RETURN(conn,result); |
---|
1210 | } |
---|
1211 | |
---|
1212 | |
---|
1213 | /* perform one step of the SASL exchange |
---|
1214 | * inputlen & input -- client data |
---|
1215 | * NULL on first step if no optional client step |
---|
1216 | * outputlen & output -- set to the server data to transmit |
---|
1217 | * to the client in the next step |
---|
1218 | * (library handles freeing this) |
---|
1219 | * |
---|
1220 | * returns: |
---|
1221 | * SASL_OK -- exchange is complete. |
---|
1222 | * SASL_CONTINUE -- indicates another step is necessary. |
---|
1223 | * SASL_TRANS -- entry for user exists, but not for mechanism |
---|
1224 | * and transition is possible |
---|
1225 | * SASL_BADPARAM -- service name needed |
---|
1226 | * SASL_BADPROT -- invalid input from client |
---|
1227 | * ... |
---|
1228 | */ |
---|
1229 | |
---|
1230 | int sasl_server_step(sasl_conn_t *conn, |
---|
1231 | const char *clientin, |
---|
1232 | unsigned clientinlen, |
---|
1233 | const char **serverout, |
---|
1234 | unsigned *serveroutlen) |
---|
1235 | { |
---|
1236 | int ret; |
---|
1237 | sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */ |
---|
1238 | |
---|
1239 | /* check parameters */ |
---|
1240 | if (_sasl_server_active==0) return SASL_NOTINIT; |
---|
1241 | if (!conn) return SASL_BADPARAM; |
---|
1242 | if ((clientin==NULL) && (clientinlen>0)) |
---|
1243 | PARAMERROR(conn); |
---|
1244 | |
---|
1245 | /* If we've already done the last send, return! */ |
---|
1246 | if(s_conn->sent_last == 1) { |
---|
1247 | return SASL_OK; |
---|
1248 | } |
---|
1249 | |
---|
1250 | /* Don't do another step if the plugin told us that we're done */ |
---|
1251 | if (conn->oparams.doneflag) { |
---|
1252 | _sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag"); |
---|
1253 | return SASL_FAIL; |
---|
1254 | } |
---|
1255 | |
---|
1256 | if(serverout) *serverout = NULL; |
---|
1257 | if(serveroutlen) *serveroutlen = 0; |
---|
1258 | |
---|
1259 | ret = s_conn->mech->plug->mech_step(conn->context, |
---|
1260 | s_conn->sparams, |
---|
1261 | clientin, |
---|
1262 | clientinlen, |
---|
1263 | serverout, |
---|
1264 | serveroutlen, |
---|
1265 | &conn->oparams); |
---|
1266 | |
---|
1267 | if (ret == SASL_OK) { |
---|
1268 | ret = do_authorization(s_conn); |
---|
1269 | } |
---|
1270 | |
---|
1271 | if (ret == SASL_OK) { |
---|
1272 | /* if we're done, we need to watch out for the following: |
---|
1273 | * 1. the mech does server-send-last |
---|
1274 | * 2. the protocol does not |
---|
1275 | * |
---|
1276 | * in this case, return SASL_CONTINUE and remember we are done. |
---|
1277 | */ |
---|
1278 | if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) { |
---|
1279 | s_conn->sent_last = 1; |
---|
1280 | ret = SASL_CONTINUE; |
---|
1281 | } |
---|
1282 | if(!conn->oparams.maxoutbuf) { |
---|
1283 | conn->oparams.maxoutbuf = conn->props.maxbufsize; |
---|
1284 | } |
---|
1285 | |
---|
1286 | if(conn->oparams.user == NULL || conn->oparams.authid == NULL) { |
---|
1287 | sasl_seterror(conn, 0, |
---|
1288 | "mech did not call canon_user for both authzid " \ |
---|
1289 | "and authid"); |
---|
1290 | ret = SASL_BADPROT; |
---|
1291 | } |
---|
1292 | } |
---|
1293 | |
---|
1294 | if( ret != SASL_OK |
---|
1295 | && ret != SASL_CONTINUE |
---|
1296 | && ret != SASL_INTERACT) { |
---|
1297 | if(conn->context) { |
---|
1298 | s_conn->mech->plug->mech_dispose(conn->context, |
---|
1299 | s_conn->sparams->utils); |
---|
1300 | conn->context = NULL; |
---|
1301 | } |
---|
1302 | } |
---|
1303 | |
---|
1304 | RETURN(conn, ret); |
---|
1305 | } |
---|
1306 | |
---|
1307 | /* returns the length of all the mechanisms |
---|
1308 | * added up |
---|
1309 | */ |
---|
1310 | |
---|
1311 | static unsigned mech_names_len() |
---|
1312 | { |
---|
1313 | mechanism_t *listptr; |
---|
1314 | unsigned result = 0; |
---|
1315 | |
---|
1316 | for (listptr = mechlist->mech_list; |
---|
1317 | listptr; |
---|
1318 | listptr = listptr->next) |
---|
1319 | result += strlen(listptr->plug->mech_name); |
---|
1320 | |
---|
1321 | return result; |
---|
1322 | } |
---|
1323 | |
---|
1324 | /* This returns a list of mechanisms in a NUL-terminated string |
---|
1325 | * |
---|
1326 | * The default behavior is to seperate with spaces if sep==NULL |
---|
1327 | */ |
---|
1328 | int _sasl_server_listmech(sasl_conn_t *conn, |
---|
1329 | const char *user __attribute__((unused)), |
---|
1330 | const char *prefix, |
---|
1331 | const char *sep, |
---|
1332 | const char *suffix, |
---|
1333 | const char **result, |
---|
1334 | unsigned *plen, |
---|
1335 | int *pcount) |
---|
1336 | { |
---|
1337 | int lup; |
---|
1338 | mechanism_t *listptr; |
---|
1339 | int ret; |
---|
1340 | int resultlen; |
---|
1341 | int flag; |
---|
1342 | const char *mysep; |
---|
1343 | |
---|
1344 | /* if there hasn't been a sasl_sever_init() fail */ |
---|
1345 | if (_sasl_server_active==0) return SASL_NOTINIT; |
---|
1346 | if (!conn) return SASL_BADPARAM; |
---|
1347 | if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn); |
---|
1348 | |
---|
1349 | if (! result) |
---|
1350 | PARAMERROR(conn); |
---|
1351 | |
---|
1352 | if (plen != NULL) |
---|
1353 | *plen = 0; |
---|
1354 | if (pcount != NULL) |
---|
1355 | *pcount = 0; |
---|
1356 | |
---|
1357 | if (sep) { |
---|
1358 | mysep = sep; |
---|
1359 | } else { |
---|
1360 | mysep = " "; |
---|
1361 | } |
---|
1362 | |
---|
1363 | if (! mechlist || mechlist->mech_length <= 0) |
---|
1364 | INTERROR(conn, SASL_NOMECH); |
---|
1365 | |
---|
1366 | resultlen = (prefix ? strlen(prefix) : 0) |
---|
1367 | + (strlen(mysep) * (mechlist->mech_length - 1)) |
---|
1368 | + mech_names_len() |
---|
1369 | + (suffix ? strlen(suffix) : 0) |
---|
1370 | + 1; |
---|
1371 | ret = _buf_alloc(&conn->mechlist_buf, |
---|
1372 | &conn->mechlist_buf_len, resultlen); |
---|
1373 | if(ret != SASL_OK) MEMERROR(conn); |
---|
1374 | |
---|
1375 | if (prefix) |
---|
1376 | strcpy (conn->mechlist_buf,prefix); |
---|
1377 | else |
---|
1378 | *(conn->mechlist_buf) = '\0'; |
---|
1379 | |
---|
1380 | listptr = mechlist->mech_list; |
---|
1381 | |
---|
1382 | flag = 0; |
---|
1383 | /* make list */ |
---|
1384 | for (lup = 0; lup < mechlist->mech_length; lup++) { |
---|
1385 | /* currently, we don't use the "user" parameter for anything */ |
---|
1386 | if (mech_permitted(conn, listptr)) { |
---|
1387 | if (pcount != NULL) |
---|
1388 | (*pcount)++; |
---|
1389 | |
---|
1390 | /* print seperator */ |
---|
1391 | if (flag) { |
---|
1392 | strcat(conn->mechlist_buf, mysep); |
---|
1393 | } else { |
---|
1394 | flag = 1; |
---|
1395 | } |
---|
1396 | |
---|
1397 | /* now print the mechanism name */ |
---|
1398 | strcat(conn->mechlist_buf, listptr->plug->mech_name); |
---|
1399 | } |
---|
1400 | |
---|
1401 | listptr = listptr->next; |
---|
1402 | } |
---|
1403 | |
---|
1404 | if (suffix) |
---|
1405 | strcat(conn->mechlist_buf,suffix); |
---|
1406 | |
---|
1407 | if (plen!=NULL) |
---|
1408 | *plen=strlen(conn->mechlist_buf); |
---|
1409 | |
---|
1410 | *result = conn->mechlist_buf; |
---|
1411 | |
---|
1412 | return SASL_OK; |
---|
1413 | |
---|
1414 | } |
---|
1415 | |
---|
1416 | sasl_string_list_t *_sasl_server_mechs(void) |
---|
1417 | { |
---|
1418 | mechanism_t *listptr; |
---|
1419 | sasl_string_list_t *retval = NULL, *next=NULL; |
---|
1420 | |
---|
1421 | if(!_sasl_server_active) return NULL; |
---|
1422 | |
---|
1423 | /* make list */ |
---|
1424 | for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) { |
---|
1425 | next = sasl_ALLOC(sizeof(sasl_string_list_t)); |
---|
1426 | |
---|
1427 | if(!next && !retval) return NULL; |
---|
1428 | else if(!next) { |
---|
1429 | next = retval->next; |
---|
1430 | do { |
---|
1431 | sasl_FREE(retval); |
---|
1432 | retval = next; |
---|
1433 | next = retval->next; |
---|
1434 | } while(next); |
---|
1435 | return NULL; |
---|
1436 | } |
---|
1437 | |
---|
1438 | next->d = listptr->plug->mech_name; |
---|
1439 | |
---|
1440 | if(!retval) { |
---|
1441 | next->next = NULL; |
---|
1442 | retval = next; |
---|
1443 | } else { |
---|
1444 | next->next = retval; |
---|
1445 | retval = next; |
---|
1446 | } |
---|
1447 | } |
---|
1448 | |
---|
1449 | return retval; |
---|
1450 | } |
---|
1451 | |
---|
1452 | #define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t')) |
---|
1453 | static int is_mech(const char *t, const char *m) |
---|
1454 | { |
---|
1455 | int sl = strlen(m); |
---|
1456 | return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl)); |
---|
1457 | } |
---|
1458 | |
---|
1459 | /* returns OK if it's valid */ |
---|
1460 | static int _sasl_checkpass(sasl_conn_t *conn, const char *service, |
---|
1461 | const char *user, const char *pass) |
---|
1462 | { |
---|
1463 | sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; |
---|
1464 | int result; |
---|
1465 | sasl_getopt_t *getopt; |
---|
1466 | sasl_server_userdb_checkpass_t *checkpass_cb; |
---|
1467 | void *context; |
---|
1468 | const char *mlist, *mech; |
---|
1469 | struct sasl_verify_password_s *v; |
---|
1470 | |
---|
1471 | /* call userdb callback function, if available */ |
---|
1472 | result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS, |
---|
1473 | &checkpass_cb, &context); |
---|
1474 | if(result == SASL_OK && checkpass_cb) { |
---|
1475 | result = checkpass_cb(conn, context, user, pass, strlen(pass), |
---|
1476 | s_conn->sparams->propctx); |
---|
1477 | if(result == SASL_OK) |
---|
1478 | return SASL_OK; |
---|
1479 | } |
---|
1480 | |
---|
1481 | /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */ |
---|
1482 | if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) |
---|
1483 | == SASL_OK) { |
---|
1484 | getopt(context, NULL, "pwcheck_method", &mlist, NULL); |
---|
1485 | } |
---|
1486 | |
---|
1487 | if(!mlist) mlist = DEFAULT_CHECKPASS_MECH; |
---|
1488 | |
---|
1489 | result = SASL_NOMECH; |
---|
1490 | |
---|
1491 | mech = mlist; |
---|
1492 | while (*mech && result != SASL_OK) { |
---|
1493 | for (v = _sasl_verify_password; v->name; v++) { |
---|
1494 | if(is_mech(mech, v->name)) { |
---|
1495 | result = v->verify(conn, user, pass, service, |
---|
1496 | s_conn->user_realm); |
---|
1497 | break; |
---|
1498 | } |
---|
1499 | } |
---|
1500 | if (result != SASL_OK) { |
---|
1501 | /* skip to next mech in list */ |
---|
1502 | while (*mech && !isspace((int) *mech)) mech++; |
---|
1503 | while (*mech && isspace((int) *mech)) mech++; |
---|
1504 | } |
---|
1505 | } |
---|
1506 | |
---|
1507 | if (result == SASL_NOMECH) { |
---|
1508 | /* no mechanism available ?!? */ |
---|
1509 | _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech); |
---|
1510 | } |
---|
1511 | |
---|
1512 | if (result != SASL_OK) |
---|
1513 | sasl_seterror(conn, SASL_NOLOG, "checkpass failed"); |
---|
1514 | |
---|
1515 | RETURN(conn, result); |
---|
1516 | } |
---|
1517 | |
---|
1518 | /* check if a plaintext password is valid |
---|
1519 | * if user is NULL, check if plaintext passwords are enabled |
---|
1520 | * inputs: |
---|
1521 | * user -- user to query in current user_domain |
---|
1522 | * userlen -- length of username, 0 = strlen(user) |
---|
1523 | * pass -- plaintext password to check |
---|
1524 | * passlen -- length of password, 0 = strlen(pass) |
---|
1525 | * returns |
---|
1526 | * SASL_OK -- success |
---|
1527 | * SASL_NOMECH -- mechanism not supported |
---|
1528 | * SASL_NOVERIFY -- user found, but no verifier |
---|
1529 | * SASL_NOUSER -- user not found |
---|
1530 | */ |
---|
1531 | int sasl_checkpass(sasl_conn_t *conn, |
---|
1532 | const char *user, |
---|
1533 | unsigned userlen __attribute__((unused)), |
---|
1534 | const char *pass, |
---|
1535 | unsigned passlen) |
---|
1536 | { |
---|
1537 | int result; |
---|
1538 | |
---|
1539 | if (_sasl_server_active==0) return SASL_NOTINIT; |
---|
1540 | |
---|
1541 | /* check if it's just a query if we are enabled */ |
---|
1542 | if (!user) |
---|
1543 | return SASL_OK; |
---|
1544 | |
---|
1545 | if (!conn) return SASL_BADPARAM; |
---|
1546 | |
---|
1547 | /* check params */ |
---|
1548 | if (pass == NULL) |
---|
1549 | PARAMERROR(conn); |
---|
1550 | |
---|
1551 | result = _sasl_checkpass(conn, conn->service, user, pass); |
---|
1552 | |
---|
1553 | if (result == SASL_OK) { |
---|
1554 | strncpy(conn->authid_buf, user, CANON_BUF_SIZE); |
---|
1555 | conn->oparams.authid = conn->authid_buf; |
---|
1556 | |
---|
1557 | result = _sasl_transition(conn, pass, passlen); |
---|
1558 | } |
---|
1559 | |
---|
1560 | RETURN(conn,result); |
---|
1561 | } |
---|
1562 | |
---|
1563 | /* check if a user exists on server |
---|
1564 | * conn -- connection context (may be NULL, used to hold last error) |
---|
1565 | * service -- registered name of the service using SASL (e.g. "imap") |
---|
1566 | * user_realm -- permits multiple user realms on server, NULL = default |
---|
1567 | * user -- NUL terminated user name |
---|
1568 | * |
---|
1569 | * returns: |
---|
1570 | * SASL_OK -- success |
---|
1571 | * SASL_DISABLED -- account disabled [FIXME: currently not detected] |
---|
1572 | * SASL_NOUSER -- user not found |
---|
1573 | * SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported] |
---|
1574 | * SASL_NOMECH -- no mechanisms enabled |
---|
1575 | */ |
---|
1576 | int sasl_user_exists(sasl_conn_t *conn, |
---|
1577 | const char *service, |
---|
1578 | const char *user_realm, |
---|
1579 | const char *user) |
---|
1580 | { |
---|
1581 | int result=SASL_NOMECH; |
---|
1582 | const char *mlist, *mech; |
---|
1583 | void *context; |
---|
1584 | sasl_getopt_t *getopt; |
---|
1585 | struct sasl_verify_password_s *v; |
---|
1586 | |
---|
1587 | /* check params */ |
---|
1588 | if (_sasl_server_active==0) return SASL_NOTINIT; |
---|
1589 | if (!conn) return SASL_BADPARAM; |
---|
1590 | if (!user || conn->type != SASL_CONN_SERVER) |
---|
1591 | PARAMERROR(conn); |
---|
1592 | |
---|
1593 | if(!service) service = conn->service; |
---|
1594 | |
---|
1595 | /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */ |
---|
1596 | if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) |
---|
1597 | == SASL_OK) { |
---|
1598 | getopt(context, NULL, "pwcheck_method", &mlist, NULL); |
---|
1599 | } |
---|
1600 | |
---|
1601 | if(!mlist) mlist = DEFAULT_CHECKPASS_MECH; |
---|
1602 | |
---|
1603 | result = SASL_NOMECH; |
---|
1604 | |
---|
1605 | mech = mlist; |
---|
1606 | while (*mech && result != SASL_OK) { |
---|
1607 | for (v = _sasl_verify_password; v->name; v++) { |
---|
1608 | if(is_mech(mech, v->name)) { |
---|
1609 | result = v->verify(conn, user, NULL, service, user_realm); |
---|
1610 | break; |
---|
1611 | } |
---|
1612 | } |
---|
1613 | if (result != SASL_OK) { |
---|
1614 | /* skip to next mech in list */ |
---|
1615 | while (*mech && !isspace((int) *mech)) mech++; |
---|
1616 | while (*mech && isspace((int) *mech)) mech++; |
---|
1617 | } |
---|
1618 | } |
---|
1619 | |
---|
1620 | /* Screen out the SASL_BADPARAM response |
---|
1621 | * we'll get from not giving a password */ |
---|
1622 | if(result == SASL_BADPARAM) { |
---|
1623 | result = SASL_OK; |
---|
1624 | } |
---|
1625 | |
---|
1626 | if (result == SASL_NOMECH) { |
---|
1627 | /* no mechanism available ?!? */ |
---|
1628 | _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?"); |
---|
1629 | sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?"); |
---|
1630 | } |
---|
1631 | |
---|
1632 | RETURN(conn, result); |
---|
1633 | } |
---|
1634 | |
---|
1635 | /* check if an apop exchange is valid |
---|
1636 | * (note this is an optional part of the SASL API) |
---|
1637 | * if challenge is NULL, just check if APOP is enabled |
---|
1638 | * inputs: |
---|
1639 | * challenge -- challenge which was sent to client |
---|
1640 | * challen -- length of challenge, 0 = strlen(challenge) |
---|
1641 | * response -- client response, "<user> <digest>" (RFC 1939) |
---|
1642 | * resplen -- length of response, 0 = strlen(response) |
---|
1643 | * returns |
---|
1644 | * SASL_OK -- success |
---|
1645 | * SASL_BADAUTH -- authentication failed |
---|
1646 | * SASL_BADPARAM -- missing challenge |
---|
1647 | * SASL_BADPROT -- protocol error (e.g., response in wrong format) |
---|
1648 | * SASL_NOVERIFY -- user found, but no verifier |
---|
1649 | * SASL_NOMECH -- mechanism not supported |
---|
1650 | * SASL_NOUSER -- user not found |
---|
1651 | */ |
---|
1652 | int sasl_checkapop(sasl_conn_t *conn, |
---|
1653 | #ifdef DO_SASL_CHECKAPOP |
---|
1654 | const char *challenge, |
---|
1655 | unsigned challen __attribute__((unused)), |
---|
1656 | const char *response, |
---|
1657 | unsigned resplen __attribute__((unused))) |
---|
1658 | #else |
---|
1659 | const char *challenge __attribute__((unused)), |
---|
1660 | unsigned challen __attribute__((unused)), |
---|
1661 | const char *response __attribute__((unused)), |
---|
1662 | unsigned resplen __attribute__((unused))) |
---|
1663 | #endif |
---|
1664 | { |
---|
1665 | #ifdef DO_SASL_CHECKAPOP |
---|
1666 | sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; |
---|
1667 | char *user, *user_end; |
---|
1668 | const char *password_request[] = { SASL_AUX_PASSWORD, NULL }; |
---|
1669 | size_t user_len; |
---|
1670 | int result; |
---|
1671 | |
---|
1672 | if (_sasl_server_active==0) |
---|
1673 | return SASL_NOTINIT; |
---|
1674 | |
---|
1675 | /* check if it's just a query if we are enabled */ |
---|
1676 | if(!challenge) |
---|
1677 | return SASL_OK; |
---|
1678 | |
---|
1679 | /* check params */ |
---|
1680 | if (!conn) return SASL_BADPARAM; |
---|
1681 | if (!response) |
---|
1682 | PARAMERROR(conn); |
---|
1683 | |
---|
1684 | /* Parse out username and digest. |
---|
1685 | * |
---|
1686 | * Per RFC 1939, response must be "<user> <digest>", where |
---|
1687 | * <digest> is a 16-octet value which is sent in hexadecimal |
---|
1688 | * format, using lower-case ASCII characters. |
---|
1689 | */ |
---|
1690 | user_end = strrchr(response, ' '); |
---|
1691 | if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32) |
---|
1692 | { |
---|
1693 | sasl_seterror(conn, 0, "Bad Digest"); |
---|
1694 | RETURN(conn,SASL_BADPROT); |
---|
1695 | } |
---|
1696 | |
---|
1697 | user_len = (size_t)(user_end - response); |
---|
1698 | user = sasl_ALLOC(user_len + 1); |
---|
1699 | memcpy(user, response, user_len); |
---|
1700 | user[user_len] = '\0'; |
---|
1701 | |
---|
1702 | result = prop_request(s_conn->sparams->propctx, password_request); |
---|
1703 | if(result != SASL_OK) |
---|
1704 | { |
---|
1705 | sasl_FREE(user); |
---|
1706 | RETURN(conn, result); |
---|
1707 | } |
---|
1708 | |
---|
1709 | /* Cannonify it */ |
---|
1710 | result = _sasl_canon_user(conn, user, user_len, |
---|
1711 | SASL_CU_AUTHID | SASL_CU_AUTHZID, |
---|
1712 | &(conn->oparams)); |
---|
1713 | sasl_FREE(user); |
---|
1714 | |
---|
1715 | if(result != SASL_OK) RETURN(conn, result); |
---|
1716 | |
---|
1717 | /* Do APOP verification */ |
---|
1718 | result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid, |
---|
1719 | challenge, user_end + 1, s_conn->user_realm); |
---|
1720 | |
---|
1721 | /* If verification failed, we don't want to encourage getprop to work */ |
---|
1722 | if(result != SASL_OK) { |
---|
1723 | conn->oparams.user = NULL; |
---|
1724 | conn->oparams.authid = NULL; |
---|
1725 | } |
---|
1726 | |
---|
1727 | RETURN(conn, result); |
---|
1728 | #else /* sasl_checkapop was disabled at compile time */ |
---|
1729 | sasl_seterror(conn, SASL_NOLOG, |
---|
1730 | "sasl_checkapop called, but was disabled at compile time"); |
---|
1731 | RETURN(conn, SASL_NOMECH); |
---|
1732 | #endif /* DO_SASL_CHECKAPOP */ |
---|
1733 | } |
---|
1734 | |
---|