1 /* SASL server API implementation
4 * $Id: server.c,v 1.146 2006/04/26 17:45:53 murch Exp $
7 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
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
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
28 * Pittsburgh, PA 15213-3890
29 * (412) 268-4387, fax: (412) 268-7395
30 * tech-transfer@andrew.cmu.edu
32 * 4. Redistributions of any form whatsoever must retain the following
34 * "This product includes software developed by Computing Services
35 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
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.
46 /* local functions/structs don't start with sasl
54 #include <sys/types.h>
67 /* gotta define gethostname ourselves on suns */
68 extern int gethostname(char *, int);
71 #define DEFAULT_CHECKPASS_MECH "auxprop"
73 /* Contains functions:
86 /* if we've initialized the server sucessfully */
87 static int _sasl_server_active = 0;
89 /* For access by other modules */
90 int _is_sasl_server_active(void) { return _sasl_server_active; }
92 static int _sasl_checkpass(sasl_conn_t *conn,
93 const char *user, unsigned userlen,
94 const char *pass, unsigned passlen);
96 static mech_list_t *mechlist = NULL; /* global var which holds the list */
98 sasl_global_callbacks_t global_callbacks;
100 /* set the password for a user
101 * conn -- SASL connection
103 * pass -- plaintext password, may be NULL to remove user
104 * passlen -- length of password, 0 = strlen(pass)
105 * oldpass -- NULL will sometimes work
106 * oldpasslen -- length of password, 0 = strlen(oldpass)
107 * flags -- see flags below
110 * SASL_NOCHANGE -- proper entry already exists
111 * SASL_NOMECH -- no authdb supports password setting as configured
112 * SASL_NOVERIFY -- user exists, but no settable password present
113 * SASL_DISABLED -- account disabled
114 * SASL_PWLOCK -- password locked
115 * SASL_WEAKPASS -- password too weak for security policy
116 * SASL_NOUSERPASS -- user-supplied passwords not permitted
117 * SASL_FAIL -- OS error
118 * SASL_BADPARAM -- password too long
119 * SASL_OK -- successful
122 int sasl_setpass(sasl_conn_t *conn,
124 const char *pass, unsigned passlen,
129 int result = SASL_OK, tmpresult;
130 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
131 const char *password_request[] = { SASL_AUX_PASSWORD_PROP, NULL };
132 sasl_server_userdb_setpass_t *setpass_cb = NULL;
133 void *context = NULL;
134 int tried_setpass = 0;
136 server_sasl_mechanism_t *m;
139 if (!_sasl_server_active || !mechlist) return SASL_NOTINIT;
142 if (!conn) return SASL_BADPARAM;
143 if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
145 if ((!(flags & SASL_SET_DISABLE) && passlen == 0)
146 || ((flags & SASL_SET_CREATE) && (flags & SASL_SET_DISABLE)))
149 /* Check that we have an active SASL mechanism */
150 if (sasl_getprop (conn,
152 (const void **) ¤t_mech) != SASL_OK) {
156 if ( (flags & SASL_SET_CURMECH_ONLY) &&
157 (current_mech == NULL) ) {
158 sasl_seterror( conn, SASL_NOLOG,
159 "No current SASL mechanism available");
160 RETURN(conn, SASL_BADPARAM);
163 /* Do we want to store SASL_AUX_PASSWORD_PROP (plain text)? and
164 * Do we have an auxprop backend that can store properties?
166 if ((flags & SASL_SET_DISABLE || !(flags & SASL_SET_NOPLAIN)) &&
167 sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) {
171 if (flags & SASL_SET_DISABLE) {
176 result = prop_request(s_conn->sparams->propctx, password_request);
177 if (result == SASL_OK) {
178 result = prop_set(s_conn->sparams->propctx, SASL_AUX_PASSWORD_PROP,
181 if (result == SASL_OK) {
182 result = sasl_auxprop_store(conn, s_conn->sparams->propctx, user);
184 if (result != SASL_OK) {
185 _sasl_log(conn, SASL_LOG_ERR,
186 "setpass failed for %s: %z",
189 _sasl_log(conn, SASL_LOG_NOTE,
190 "setpass succeeded for %s", user);
194 /* We want to preserve the current value of result, so we use tmpresult below */
196 /* call userdb callback function */
197 tmpresult = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_SETPASS,
198 &setpass_cb, &context);
199 if (tmpresult == SASL_OK && setpass_cb) {
203 tmpresult = setpass_cb(conn, context, user, pass, passlen,
204 s_conn->sparams->propctx, flags);
205 if(tmpresult != SASL_OK) {
207 _sasl_log(conn, SASL_LOG_ERR,
208 "setpass callback failed for %s: %z",
211 _sasl_log(conn, SASL_LOG_NOTE,
212 "setpass callback succeeded for %s", user);
216 /* now we let the mechanisms set their secrets */
217 for (sm = mechlist->mech_list; sm; sm = sm->next) {
220 if (!m->plug->setpass) {
221 /* can't set pass for this mech */
225 /* Invoke only one setpass for the currently selected mechanism,
226 if SASL_SET_CURMECH_ONLY is specified */
227 if ((flags & SASL_SET_CURMECH_ONLY) &&
228 (strcmp(current_mech, m->plug->mech_name) != 0)) {
234 tmpresult = m->plug->setpass(m->plug->glob_context,
235 ((sasl_server_conn_t *)conn)->sparams,
241 if (tmpresult == SASL_OK) {
242 _sasl_log(conn, SASL_LOG_NOTE,
243 "%s: set secret for %s", m->plug->mech_name, user);
245 m->condition = SASL_OK; /* if we previously thought the
246 mechanism didn't have any user secrets
247 we now think it does */
249 } else if (tmpresult == SASL_NOCHANGE) {
250 _sasl_log(conn, SASL_LOG_NOTE,
251 "%s: secret not changed for %s", m->plug->mech_name, user);
254 _sasl_log(conn, SASL_LOG_ERR,
255 "%s: failed to set secret for %s: %z (%m)",
256 m->plug->mech_name, user, tmpresult,
266 if (!tried_setpass) {
267 _sasl_log(conn, SASL_LOG_WARN,
268 "secret not changed for %s: "
269 "no writable auxprop plugin or setpass callback found",
273 RETURN(conn, result);
276 /* local mechanism which disposes of server */
277 static void server_dispose(sasl_conn_t *pconn)
279 sasl_server_conn_t *s_conn= (sasl_server_conn_t *) pconn;
280 context_list_t *cur, *cur_next;
283 && s_conn->mech->m.plug->mech_dispose) {
284 s_conn->mech->m.plug->mech_dispose(pconn->context,
285 s_conn->sparams->utils);
287 pconn->context = NULL;
289 for(cur = s_conn->mech_contexts; cur; cur=cur_next) {
290 cur_next = cur->next;
292 cur->mech->m.plug->mech_dispose(cur->context, s_conn->sparams->utils);
295 s_conn->mech_contexts = NULL;
297 _sasl_free_utils(&s_conn->sparams->utils);
299 if (s_conn->sparams->propctx)
300 prop_dispose(&s_conn->sparams->propctx);
303 sasl_FREE(s_conn->appname);
305 if (s_conn->user_realm)
306 sasl_FREE(s_conn->user_realm);
309 sasl_FREE(s_conn->sparams);
311 _sasl_conn_dispose(pconn);
314 static int init_mechlist(void)
316 sasl_utils_t *newutils = NULL;
318 mechlist->mutex = sasl_MUTEX_ALLOC();
319 if(!mechlist->mutex) return SASL_FAIL;
321 /* set util functions - need to do rest */
322 newutils = _sasl_alloc_utils(NULL, &global_callbacks);
323 if (newutils == NULL)
326 newutils->checkpass = &_sasl_checkpass;
328 mechlist->utils = newutils;
329 mechlist->mech_list=NULL;
330 mechlist->mech_length=0;
339 int sasl_server_add_plugin(const char *plugname,
340 sasl_server_plug_init_t *p)
343 sasl_server_plug_t *pluglist;
345 sasl_server_plug_init_t *entry_point;
350 if(!plugname || !p) return SASL_BADPARAM;
352 entry_point = (sasl_server_plug_init_t *)p;
354 /* call into the shared library asking for information about it */
355 /* version is filled in with the version of the plugin */
356 result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION, &version,
357 &pluglist, &plugcount);
359 if ((result != SASL_OK) && (result != SASL_NOUSER)
360 && (result != SASL_CONTINUE)) {
361 _sasl_log(NULL, SASL_LOG_DEBUG,
362 "server add_plugin entry_point error %z\n", result);
366 /* Make sure plugin is using the same SASL version as us */
367 if (version != SASL_SERVER_PLUG_VERSION)
369 _sasl_log(NULL, SASL_LOG_ERR,
370 "version mismatch on plugin");
374 for (lupe=0;lupe < plugcount ;lupe++)
376 mech = sasl_ALLOC(sizeof(mechanism_t));
377 if (! mech) return SASL_NOMEM;
378 memset (mech, 0, sizeof(mechanism_t));
380 mech->m.plug = pluglist++;
381 if(_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) {
385 mech->m.version = version;
387 /* wheather this mech actually has any users in it's db */
388 mech->m.condition = result; /* SASL_OK, SASL_CONTINUE or SASL_NOUSER */
390 /* mech->m.f = NULL; */
392 mech->next = mechlist->mech_list;
393 mechlist->mech_list = mech;
394 mechlist->mech_length++;
400 static int server_done(void) {
404 if(_sasl_server_active == 0)
407 _sasl_server_active--;
409 if(_sasl_server_active) {
410 /* Don't de-init yet! Our refcount is nonzero. */
411 return SASL_CONTINUE;
414 if (mechlist != NULL)
416 m=mechlist->mech_list; /* m point to beginning of the list */
423 if (prevm->m.plug->mech_free) {
424 prevm->m.plug->mech_free(prevm->m.plug->glob_context,
428 sasl_FREE(prevm->m.plugname);
431 _sasl_free_utils(&mechlist->utils);
432 sasl_MUTEX_FREE(mechlist->mutex);
437 /* Free the auxprop plugins */
438 _sasl_auxprop_free();
440 global_callbacks.callbacks = NULL;
441 global_callbacks.appname = NULL;
446 static int server_idle(sasl_conn_t *conn)
452 for (m = mechlist->mech_list;
456 && m->m.plug->idle(m->m.plug->glob_context,
458 conn ? ((sasl_server_conn_t *)conn)->sparams : NULL))
464 static int load_config(const sasl_callback_t *verifyfile_cb)
467 const char *path_to_config = NULL;
469 char *config_filename = NULL;
471 const sasl_callback_t *getconfpath_cb = NULL;
474 /* If appname was not provided, behave as if there is no config file
475 (see also sasl_config_init() */
476 if (global_callbacks.appname == NULL) {
477 return SASL_CONTINUE;
480 /* get the path to the config file */
481 getconfpath_cb = _sasl_find_getconfpath_callback( global_callbacks.callbacks );
482 if (getconfpath_cb == NULL) return SASL_BADPARAM;
484 /* getconfpath_cb->proc MUST be a sasl_getconfpath_t; if only C had a type
486 result = ((sasl_getconfpath_t *)(getconfpath_cb->proc))(getconfpath_cb->context,
488 if (result != SASL_OK) goto done;
489 if (path_to_config == NULL) path_to_config = "";
491 next = path_to_config;
493 while (next != NULL) {
494 next = strchr(path_to_config, PATHS_DELIMITER);
496 /* length = length of path + '/' + length of appname + ".conf" + 1
500 path_len = next - path_to_config;
501 next++; /* Skip to the next path */
503 path_len = strlen(path_to_config);
506 len = path_len + 2 + strlen(global_callbacks.appname) + 5 + 1;
508 if (len > PATH_MAX ) {
513 /* construct the filename for the config file */
514 config_filename = sasl_ALLOC((unsigned)len);
515 if (! config_filename) {
520 snprintf(config_filename, len, "%.*s%c%s.conf", path_len, path_to_config,
521 HIER_DELIMITER, global_callbacks.appname);
523 /* Ask the application if it's safe to use this file */
524 result = ((sasl_verifyfile_t *)(verifyfile_cb->proc))(verifyfile_cb->context,
525 config_filename, SASL_VRFY_CONF);
527 /* returns SASL_CONTINUE if the config file doesn't exist */
528 if (result == SASL_OK) {
529 result = sasl_config_init(config_filename);
531 if (result != SASL_CONTINUE) {
537 if (config_filename) {
538 sasl_FREE(config_filename);
539 config_filename = NULL;
542 path_to_config = next;
546 if (config_filename) sasl_FREE(config_filename);
552 * Verify that all the callbacks are valid
554 static int verify_server_callbacks(const sasl_callback_t *callbacks)
556 if (callbacks == NULL) return SASL_OK;
558 while (callbacks->id != SASL_CB_LIST_END) {
559 if (callbacks->proc==NULL) return SASL_FAIL;
567 static char *grab_field(char *line, char **eofield)
572 while (isspace((int) *line)) line++;
574 /* find end of field */
575 while (line[d] && !isspace(((int) line[d]))) d++;
576 field = sasl_ALLOC(d + 1);
577 if (!field) { return NULL; }
578 memcpy(field, line, d);
585 struct secflag_map_s {
590 struct secflag_map_s secflag_map[] = {
591 { "noplaintext", SASL_SEC_NOPLAINTEXT },
592 { "noactive", SASL_SEC_NOACTIVE },
593 { "nodictionary", SASL_SEC_NODICTIONARY },
594 { "forward_secrecy", SASL_SEC_FORWARD_SECRECY },
595 { "noanonymous", SASL_SEC_NOANONYMOUS },
596 { "pass_credentials", SASL_SEC_PASS_CREDENTIALS },
597 { "mutual_auth", SASL_SEC_MUTUAL_AUTH },
601 static int parse_mechlist_file(const char *mechlistfile)
608 f = fopen(mechlistfile, "r");
609 if (!f) return SASL_FAIL;
612 while (fgets(buf, sizeof(buf), f) != NULL) {
613 mechanism_t *n = sasl_ALLOC(sizeof(mechanism_t));
614 sasl_server_plug_t *nplug;
616 if (n == NULL) { r = SASL_NOMEM; break; }
617 n->m.version = SASL_SERVER_PLUG_VERSION;
618 n->m.condition = SASL_CONTINUE;
619 nplug = sasl_ALLOC(sizeof(sasl_server_plug_t));
620 if (nplug == NULL) { r = SASL_NOMEM; break; }
621 memset(nplug, 0, sizeof(sasl_server_plug_t));
624 plugin-file WS mech_name WS max_ssf *(WS security_flag) RET
628 n->m.f = grab_field(buf, &ptr);
631 nplug->mech_name = grab_field(ptr, &ptr);
634 nplug->max_ssf = strtol(ptr, &ptr, 10);
636 /* grab security flags */
637 while (*ptr != '\n') {
638 struct secflag_map_s *map;
640 /* read security flag */
641 t = grab_field(ptr, &ptr);
644 if (!strcasecmp(t, map->name)) {
645 nplug->security_flags |= map->value;
651 _sasl_log(NULL, SASL_LOG_ERR,
652 "%s: couldn't identify flag '%s'",
653 nplug->mech_name, t);
658 /* insert mechanism into mechlist */
660 n->next = mechlist->mech_list;
661 mechlist->mech_list = n;
662 mechlist->mech_length++;
669 /* initialize server drivers, done once per process
670 * callbacks -- callbacks for all server connections; must include
672 * appname -- name of calling application
673 * (for lower level logging and reading of the configuration file)
675 * state -- server state
678 * SASL_BADPARAM -- error in config file
679 * SASL_NOMEM -- memory failure
680 * SASL_BADVERS -- Mechanism version mismatch
683 int sasl_server_init(const sasl_callback_t *callbacks,
687 const sasl_callback_t *vf;
688 const char *pluginfile = NULL;
690 sasl_getopt_t *getopt;
694 const add_plugin_list_t ep_list[] = {
695 { "sasl_server_plug_init", (add_plugin_t *)sasl_server_add_plugin },
696 { "sasl_auxprop_plug_init", (add_plugin_t *)sasl_auxprop_add_plugin },
697 { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
701 /* we require the appname (if present) to be short enough to be a path */
702 if (appname != NULL && strlen(appname) >= PATH_MAX)
703 return SASL_BADPARAM;
705 if (_sasl_server_active) {
706 /* We're already active, just increase our refcount */
707 /* xxx do something with the callback structure? */
708 _sasl_server_active++;
712 ret = _sasl_common_init(&global_callbacks);
716 /* verify that the callbacks look ok */
717 ret = verify_server_callbacks(callbacks);
721 global_callbacks.callbacks = callbacks;
723 /* A shared library calling sasl_server_init will pass NULL as appname.
724 This should retain the original appname. */
725 if (appname != NULL) {
726 global_callbacks.appname = appname;
729 /* If we fail now, we have to call server_done */
730 _sasl_server_active = 1;
732 /* allocate mechlist and set it to empty */
733 mechlist = sasl_ALLOC(sizeof(mech_list_t));
734 if (mechlist == NULL) {
739 ret = init_mechlist();
740 if (ret != SASL_OK) {
745 vf = _sasl_find_verifyfile_callback(callbacks);
747 /* load config file if applicable */
748 ret = load_config(vf);
749 if ((ret != SASL_OK) && (ret != SASL_CONTINUE)) {
754 /* load internal plugins */
755 sasl_server_add_plugin("EXTERNAL", &external_server_plug_init);
758 /* delayed loading of plugins? (DSO only, as it doesn't
759 * make much [any] sense to delay in the static library case) */
760 if (_sasl_getcallback(NULL, SASL_CB_GETOPT, &getopt, &context)
762 /* No sasl_conn_t was given to getcallback, so we provide the
763 * global callbacks structure */
764 ret = getopt(&global_callbacks, NULL, "plugin_list", &pluginfile, NULL);
768 if (pluginfile != NULL) {
769 /* this file should contain a list of plugins available.
770 we'll load on demand. */
772 /* Ask the application if it's safe to use this file */
773 ret = ((sasl_verifyfile_t *)(vf->proc))(vf->context,
776 if (ret != SASL_OK) {
777 _sasl_log(NULL, SASL_LOG_ERR,
778 "unable to load plugin list %s: %z", pluginfile, ret);
781 if (ret == SASL_OK) {
782 ret = parse_mechlist_file(pluginfile);
785 /* load all plugins now */
786 ret = _sasl_load_plugins(ep_list,
787 _sasl_find_getpath_callback(callbacks),
788 _sasl_find_verifyfile_callback(callbacks));
791 if (ret == SASL_OK) {
792 _sasl_server_cleanup_hook = &server_done;
793 _sasl_server_idle_hook = &server_idle;
795 ret = _sasl_build_mechlist();
804 * Once we have the users plaintext password we
805 * may want to transition them. That is put entries
806 * for them in the passwd database for other
809 * for example PLAIN -> CRAM-MD5
812 _sasl_transition(sasl_conn_t * conn,
816 const char *dotrans = "n";
817 sasl_getopt_t *getopt;
818 int result = SASL_OK;
823 return SASL_BADPARAM;
825 if (! conn->oparams.authid)
828 /* check if this is enabled: default to false */
829 if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK)
831 getopt(context, NULL, "auto_transition", &dotrans, NULL);
832 if (dotrans == NULL) dotrans = "n";
836 if (!strcmp(dotrans, "noplain")) flags |= SASL_SET_NOPLAIN;
838 if (flags || *dotrans == '1' || *dotrans == 'y' ||
839 (*dotrans == 'o' && dotrans[1] == 'n') || *dotrans == 't') {
841 _sasl_log(conn, SASL_LOG_NOTE,
842 "transitioning user %s to auxprop database",
843 conn->oparams.authid);
844 result = sasl_setpass(conn,
845 conn->oparams.authid,
848 NULL, 0, SASL_SET_CREATE | flags);
855 /* create context for a single SASL connection
856 * service -- registered name of the service using SASL (e.g. "imap")
857 * serverFQDN -- Fully qualified domain name of server. NULL means use
858 * gethostname() or equivalent.
859 * Useful for multi-homed servers.
860 * user_realm -- permits multiple user realms on server, NULL = default
861 * iplocalport -- server IPv4/IPv6 domain literal string with port
862 * (if NULL, then mechanisms requiring IPaddr are disabled)
863 * ipremoteport -- client IPv4/IPv6 domain literal string with port
864 * (if NULL, then mechanisms requiring IPaddr are disabled)
865 * callbacks -- callbacks (e.g., authorization, lang, new getopt context)
866 * flags -- usage flags (see above)
868 * pconn -- new connection context
872 * SASL_NOMEM -- not enough memory
875 int sasl_server_new(const char *service,
876 const char *serverFQDN,
877 const char *user_realm,
878 const char *iplocalport,
879 const char *ipremoteport,
880 const sasl_callback_t *callbacks,
885 sasl_server_conn_t *serverconn;
887 sasl_getopt_t *getopt;
889 const char *log_level, *auto_trans;
891 if (_sasl_server_active==0) return SASL_NOTINIT;
892 if (! pconn) return SASL_FAIL;
893 if (! service) return SASL_FAIL;
895 *pconn=sasl_ALLOC(sizeof(sasl_server_conn_t));
896 if (*pconn==NULL) return SASL_NOMEM;
898 memset(*pconn, 0, sizeof(sasl_server_conn_t));
900 serverconn = (sasl_server_conn_t *)*pconn;
903 serverconn->sparams=sasl_ALLOC(sizeof(sasl_server_params_t));
904 if (serverconn->sparams==NULL)
907 memset(serverconn->sparams, 0, sizeof(sasl_server_params_t));
909 (*pconn)->destroy_conn = &server_dispose;
910 result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_SERVER,
911 &server_idle, serverFQDN,
912 iplocalport, ipremoteport,
913 callbacks, &global_callbacks);
914 if (result != SASL_OK)
918 /* set util functions - need to do rest */
919 utils=_sasl_alloc_utils(*pconn, &global_callbacks);
925 utils->checkpass = &_sasl_checkpass;
927 /* Setup the propctx -> We'll assume the default size */
928 serverconn->sparams->propctx=prop_new(0);
929 if(!serverconn->sparams->propctx) {
934 serverconn->sparams->service = (*pconn)->service;
935 serverconn->sparams->servicelen = (unsigned) strlen((*pconn)->service);
937 if (global_callbacks.appname && global_callbacks.appname[0] != '\0') {
938 result = _sasl_strdup (global_callbacks.appname,
939 &serverconn->appname,
941 if (result != SASL_OK) {
945 serverconn->sparams->appname = serverconn->appname;
946 serverconn->sparams->applen = (unsigned) strlen(serverconn->sparams->appname);
948 serverconn->appname = NULL;
949 serverconn->sparams->appname = NULL;
950 serverconn->sparams->applen = 0;
953 serverconn->sparams->serverFQDN = (*pconn)->serverFQDN;
954 serverconn->sparams->slen = (unsigned) strlen((*pconn)->serverFQDN);
957 result = _sasl_strdup(user_realm, &serverconn->user_realm, NULL);
958 serverconn->sparams->urlen = (unsigned) strlen(user_realm);
959 serverconn->sparams->user_realm = serverconn->user_realm;
961 serverconn->user_realm = NULL;
962 /* the sparams is already zeroed */
965 serverconn->sparams->callbacks = callbacks;
967 log_level = auto_trans = NULL;
968 if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
969 getopt(context, NULL, "log_level", &log_level, NULL);
970 getopt(context, NULL, "auto_transition", &auto_trans, NULL);
972 serverconn->sparams->log_level = log_level ? atoi(log_level) : SASL_LOG_ERR;
974 serverconn->sparams->utils = utils;
977 (*auto_trans == '1' || *auto_trans == 'y' || *auto_trans == 't' ||
978 (*auto_trans == 'o' && auto_trans[1] == 'n') ||
979 !strcmp(auto_trans, "noplain")) &&
980 sasl_auxprop_store(NULL, NULL, NULL) == SASL_OK) {
981 serverconn->sparams->transition = &_sasl_transition;
984 serverconn->sparams->canon_user = &_sasl_canon_user;
985 serverconn->sparams->props = serverconn->base.props;
986 serverconn->sparams->flags = flags;
988 if(result == SASL_OK) return SASL_OK;
991 _sasl_conn_dispose(*pconn);
999 * IF mech strength + external strength < min ssf THEN FAIL
1000 * We also have to look at the security properties and make sure
1001 * that this mechanism has everything we want
1003 static int mech_permitted(sasl_conn_t *conn,
1006 sasl_server_conn_t *s_conn = (sasl_server_conn_t *)conn;
1007 const sasl_server_plug_t *plug;
1010 context_list_t *cur;
1011 sasl_getopt_t *getopt;
1013 sasl_ssf_t minssf = 0;
1015 if(!conn) return SASL_NOMECH;
1017 if(! mech || ! mech->m.plug) {
1022 plug = mech->m.plug;
1024 /* get the list of allowed mechanisms (default = all) */
1025 if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1027 const char *mlist = NULL;
1029 getopt(context, NULL, "mech_list", &mlist, NULL);
1031 /* if we have a list, check the plugin against it */
1036 for (cp = mlist; *cp && !isspace((int) *cp); cp++);
1037 if (((size_t) (cp - mlist) == strlen(plug->mech_name)) &&
1038 !strncasecmp(mlist, plug->mech_name,
1039 strlen(plug->mech_name))) {
1043 while (*mlist && isspace((int) *mlist)) mlist++;
1046 if (!*mlist) return SASL_NOMECH; /* reached EOS -> not in our list */
1050 /* setup parameters for the call to mech_avail */
1051 s_conn->sparams->serverFQDN=conn->serverFQDN;
1052 s_conn->sparams->service=conn->service;
1053 s_conn->sparams->user_realm=s_conn->user_realm;
1054 s_conn->sparams->props=conn->props;
1055 s_conn->sparams->external_ssf=conn->external.ssf;
1057 /* Check if we have banished this one already */
1058 for(cur = s_conn->mech_contexts; cur; cur=cur->next) {
1059 if(cur->mech == mech) {
1060 /* If it's not mech_avail'd, then stop now */
1061 if(!cur->context) return SASL_NOMECH;
1066 if (conn->props.min_ssf < conn->external.ssf) {
1069 minssf = conn->props.min_ssf - conn->external.ssf;
1072 /* Generic mechanism */
1073 if (plug->max_ssf < minssf) {
1074 sasl_seterror(conn, SASL_NOLOG,
1075 "mech %s is too weak", plug->mech_name);
1076 return SASL_TOOWEAK; /* too weak */
1081 && (ret = plug->mech_avail(plug->glob_context,
1082 s_conn->sparams, (void **)&context)) != SASL_OK ) {
1083 if(ret == SASL_NOMECH) {
1084 /* Mark this mech as no good for this connection */
1085 cur = sasl_ALLOC(sizeof(context_list_t));
1090 cur->context = NULL;
1092 cur->next = s_conn->mech_contexts;
1093 s_conn->mech_contexts = cur;
1096 /* SASL_NOTDONE might also get us here */
1098 /* Error should be set by mech_avail call */
1100 } else if(context) {
1101 /* Save this context */
1102 cur = sasl_ALLOC(sizeof(context_list_t));
1107 cur->context = context;
1109 cur->next = s_conn->mech_contexts;
1110 s_conn->mech_contexts = cur;
1113 /* Generic mechanism */
1114 if (plug->max_ssf < minssf) {
1115 sasl_seterror(conn, SASL_NOLOG, "too weak");
1116 return SASL_TOOWEAK; /* too weak */
1119 /* if there are no users in the secrets database we can't use this
1121 if (mech->m.condition == SASL_NOUSER) {
1122 sasl_seterror(conn, 0, "no users in secrets db");
1126 /* Can it meet our features? */
1127 if ((conn->flags & SASL_NEED_PROXY) &&
1128 !(plug->features & SASL_FEAT_ALLOWS_PROXY)) {
1132 /* security properties---if there are any flags that differ and are
1133 in what the connection are requesting, then fail */
1135 /* special case plaintext */
1136 myflags = conn->props.security_flags;
1138 /* if there's an external layer this is no longer plaintext */
1139 if ((conn->props.min_ssf <= conn->external.ssf) &&
1140 (conn->external.ssf > 1)) {
1141 myflags &= ~SASL_SEC_NOPLAINTEXT;
1144 /* do we want to special case SASL_SEC_PASS_CREDENTIALS? nah.. */
1145 if ((myflags &= (myflags ^ plug->security_flags)) != 0) {
1146 sasl_seterror(conn, SASL_NOLOG,
1147 "security flags do not match required");
1148 return (myflags & SASL_SEC_NOPLAINTEXT) ? SASL_ENCRYPT : SASL_NOMECH;
1151 /* Check Features */
1152 if(plug->features & SASL_FEAT_GETSECRET) {
1153 /* We no longer support sasl_server_{get,put}secret */
1154 sasl_seterror(conn, 0,
1155 "mech %s requires unprovided secret facility",
1164 * make the authorization
1168 static int do_authorization(sasl_server_conn_t *s_conn)
1171 sasl_authorize_t *authproc;
1174 /* now let's see if authname is allowed to proxy for username! */
1176 /* check the proxy callback */
1177 if (_sasl_getcallback(&s_conn->base, SASL_CB_PROXY_POLICY,
1178 &authproc, &auth_context) != SASL_OK) {
1179 INTERROR(&s_conn->base, SASL_NOAUTHZ);
1182 ret = authproc(&(s_conn->base), auth_context,
1183 s_conn->base.oparams.user, s_conn->base.oparams.ulen,
1184 s_conn->base.oparams.authid, s_conn->base.oparams.alen,
1186 (s_conn->user_realm ? (unsigned) strlen(s_conn->user_realm) : 0),
1187 s_conn->sparams->propctx);
1189 RETURN(&s_conn->base, ret);
1193 /* start a mechanism exchange within a connection context
1194 * mech -- the mechanism name client requested
1195 * clientin -- client initial response (NUL terminated), NULL if empty
1196 * clientinlen -- length of initial response
1197 * serverout -- initial server challenge, NULL if done
1198 * (library handles freeing this string)
1199 * serveroutlen -- length of initial server challenge
1201 * pconn -- the connection negotiation state on success
1203 * Same returns as sasl_server_step() or
1204 * SASL_NOMECH if mechanism not available.
1206 int sasl_server_start(sasl_conn_t *conn,
1208 const char *clientin,
1209 unsigned clientinlen,
1210 const char **serverout,
1211 unsigned *serveroutlen)
1213 sasl_server_conn_t *s_conn=(sasl_server_conn_t *) conn;
1215 context_list_t *cur, **prev;
1219 if (_sasl_server_active==0) return SASL_NOTINIT;
1221 /* make sure mech is valid mechanism
1222 if not return appropriate error */
1223 m=mechlist->mech_list;
1225 /* check parameters */
1226 if(!conn) return SASL_BADPARAM;
1228 if (!mech || ((clientin==NULL) && (clientinlen>0)))
1231 if(serverout) *serverout = NULL;
1232 if(serveroutlen) *serveroutlen = 0;
1235 if (_sasl_is_equal_mech(mech, m->m.plug->mech_name, &plus))
1242 sasl_seterror(conn, 0, "Couldn't find mech %s", mech);
1243 result = SASL_NOMECH;
1247 /* Make sure that we're willing to use this mech */
1248 if ((result = mech_permitted(conn, m)) != SASL_OK) {
1252 if (m->m.condition == SASL_CONTINUE) {
1253 sasl_server_plug_init_t *entry_point;
1254 void *library = NULL;
1255 sasl_server_plug_t *pluglist;
1256 int version, plugcount;
1259 /* need to load this plugin */
1260 result = _sasl_get_plugin(m->m.f,
1261 _sasl_find_verifyfile_callback(global_callbacks.callbacks),
1264 if (result == SASL_OK) {
1265 result = _sasl_locate_entry(library, "sasl_server_plug_init",
1266 (void **)&entry_point);
1269 if (result == SASL_OK) {
1270 result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION,
1271 &version, &pluglist, &plugcount);
1274 if (result == SASL_OK) {
1275 /* find the correct mechanism in this plugin */
1276 for (l = 0; l < plugcount; l++) {
1277 if (!strcasecmp(pluglist[l].mech_name,
1278 m->m.plug->mech_name)) break;
1280 if (l == plugcount) {
1281 result = SASL_NOMECH;
1284 if (result == SASL_OK) {
1285 /* check that the parameters are the same */
1286 if ((pluglist[l].max_ssf != m->m.plug->max_ssf) ||
1287 (pluglist[l].security_flags != m->m.plug->security_flags)) {
1288 _sasl_log(conn, SASL_LOG_ERR,
1289 "%s: security parameters don't match mechlist file",
1290 pluglist[l].mech_name);
1291 result = SASL_NOMECH;
1294 if (result == SASL_OK) {
1295 /* copy mechlist over */
1296 sasl_FREE((sasl_server_plug_t *) m->m.plug);
1297 m->m.plug = &pluglist[l];
1298 m->m.condition = SASL_OK;
1301 if (result != SASL_OK) {
1302 /* The library will eventually be freed, don't sweat it */
1303 RETURN(conn, result);
1307 /* We used to setup sparams HERE, but now it's done
1308 inside of mech_permitted (which is called above) */
1309 prev = &s_conn->mech_contexts;
1310 for(cur = *prev; cur; prev=&cur->next,cur=cur->next) {
1311 if(cur->mech == m) {
1313 sasl_seterror(conn, 0,
1314 "Got past mech_permitted with a disallowed mech!");
1317 /* If we find it, we need to pull cur out of the
1318 list so it won't be freed later! */
1319 (*prev)->next = cur->next;
1320 conn->context = cur->context;
1327 if(!conn->context) {
1328 /* Note that we don't hand over a new challenge */
1329 result = s_conn->mech->m.plug->mech_new(s_conn->mech->m.plug->glob_context,
1335 /* the work was already done by mech_avail! */
1339 if (result == SASL_OK) {
1341 if(s_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
1342 /* Remote sent first, but mechanism does not support it.
1343 * RFC 2222 says we fail at this point. */
1344 sasl_seterror(conn, 0,
1345 "Remote sent first but mech does not allow it.");
1346 result = SASL_BADPROT;
1348 /* Mech wants client-first, so let them have it */
1349 result = sasl_server_step(conn,
1350 clientin, clientinlen,
1351 serverout, serveroutlen);
1354 if(s_conn->mech->m.plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
1355 /* Mech wants client first anyway, so we should do that */
1358 result = SASL_CONTINUE;
1360 /* Mech wants server-first, so let them have it */
1361 result = sasl_server_step(conn,
1362 clientin, clientinlen,
1363 serverout, serveroutlen);
1369 if( result != SASL_OK
1370 && result != SASL_CONTINUE
1371 && result != SASL_INTERACT) {
1373 s_conn->mech->m.plug->mech_dispose(conn->context,
1374 s_conn->sparams->utils);
1375 conn->context = NULL;
1379 RETURN(conn,result);
1383 /* perform one step of the SASL exchange
1384 * inputlen & input -- client data
1385 * NULL on first step if no optional client step
1386 * outputlen & output -- set to the server data to transmit
1387 * to the client in the next step
1388 * (library handles freeing this)
1391 * SASL_OK -- exchange is complete.
1392 * SASL_CONTINUE -- indicates another step is necessary.
1393 * SASL_TRANS -- entry for user exists, but not for mechanism
1394 * and transition is possible
1395 * SASL_BADPARAM -- service name needed
1396 * SASL_BADPROT -- invalid input from client
1400 int sasl_server_step(sasl_conn_t *conn,
1401 const char *clientin,
1402 unsigned clientinlen,
1403 const char **serverout,
1404 unsigned *serveroutlen)
1407 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */
1409 /* check parameters */
1410 if (_sasl_server_active==0) return SASL_NOTINIT;
1411 if (!conn) return SASL_BADPARAM;
1412 if ((clientin==NULL) && (clientinlen>0))
1415 /* If we've already done the last send, return! */
1416 if(s_conn->sent_last == 1) {
1420 /* Don't do another step if the plugin told us that we're done */
1421 if (conn->oparams.doneflag) {
1422 _sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag");
1426 if(serverout) *serverout = NULL;
1427 if(serveroutlen) *serveroutlen = 0;
1429 ret = s_conn->mech->m.plug->mech_step(conn->context,
1437 if (ret == SASL_OK) {
1438 ret = do_authorization(s_conn);
1441 if (ret == SASL_OK) {
1442 /* if we're done, we need to watch out for the following:
1443 * 1. the mech does server-send-last
1444 * 2. the protocol does not
1446 * in this case, return SASL_CONTINUE and remember we are done.
1448 if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) {
1449 s_conn->sent_last = 1;
1450 ret = SASL_CONTINUE;
1452 if(!conn->oparams.maxoutbuf) {
1453 conn->oparams.maxoutbuf = conn->props.maxbufsize;
1456 /* Validate channel bindings */
1457 if (conn->oparams.chanbindingflag == SASL_CB_FLAG_NONE &&
1458 s_conn->sparams->chanbindingcrit) {
1459 sasl_seterror(conn, 0,
1460 "server requires channel binding but client provided none");
1462 } else if (conn->oparams.chanbindingflag == SASL_CB_FLAG_WANT &&
1463 SASL_CB_PRESENT(s_conn->sparams)) {
1464 sasl_seterror(conn, 0,
1465 "client incorrectly determined server had no channel binding");
1467 } else if (conn->oparams.user == NULL || conn->oparams.authid == NULL) {
1468 sasl_seterror(conn, 0,
1469 "mech did not call canon_user for both authzid " \
1476 && ret != SASL_CONTINUE
1477 && ret != SASL_INTERACT) {
1479 s_conn->mech->m.plug->mech_dispose(conn->context,
1480 s_conn->sparams->utils);
1481 conn->context = NULL;
1488 /* returns the length of all the mechanisms
1492 static unsigned mech_names_len()
1494 mechanism_t *listptr;
1495 unsigned result = 0;
1497 for (listptr = mechlist->mech_list;
1499 listptr = listptr->next)
1500 result += (unsigned) strlen(listptr->m.plug->mech_name);
1505 /* This returns a list of mechanisms in a NUL-terminated string
1507 * The default behavior is to seperate with spaces if sep==NULL
1509 int _sasl_server_listmech(sasl_conn_t *conn,
1510 const char *user __attribute__((unused)),
1514 const char **result,
1519 mechanism_t *listptr;
1524 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */
1526 /* if there hasn't been a sasl_sever_init() fail */
1527 if (_sasl_server_active==0) return SASL_NOTINIT;
1528 if (!conn) return SASL_BADPARAM;
1529 if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
1545 if (! mechlist || mechlist->mech_length <= 0)
1546 INTERROR(conn, SASL_NOMECH);
1548 resultlen = (prefix ? strlen(prefix) : 0)
1549 + (strlen(mysep) * (mechlist->mech_length - 1) * 2)
1550 + (mech_names_len() * 2) /* including -PLUS variant */
1551 + (mechlist->mech_length * (sizeof("-PLUS") - 1))
1552 + (suffix ? strlen(suffix) : 0)
1554 ret = _buf_alloc(&conn->mechlist_buf,
1555 &conn->mechlist_buf_len, resultlen);
1556 if(ret != SASL_OK) MEMERROR(conn);
1559 strcpy (conn->mechlist_buf,prefix);
1561 *(conn->mechlist_buf) = '\0';
1563 listptr = mechlist->mech_list;
1567 for (lup = 0; lup < mechlist->mech_length; lup++) {
1568 /* currently, we don't use the "user" parameter for anything */
1569 if (mech_permitted(conn, listptr) == SASL_OK) {
1573 /* print separator */
1575 strcat(conn->mechlist_buf, mysep);
1580 /* now print the mechanism name */
1581 strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1583 /* advertise -PLUS variant if mechanism and application support CB */
1584 if ((listptr->m.plug->features & SASL_FEAT_CHANNEL_BINDING) &&
1585 SASL_CB_PRESENT(s_conn->sparams)) {
1588 strcat(conn->mechlist_buf, mysep);
1589 strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1590 strcat(conn->mechlist_buf, "-PLUS");
1594 listptr = listptr->next;
1598 strcat(conn->mechlist_buf,suffix);
1601 *plen = (unsigned) strlen(conn->mechlist_buf);
1603 *result = conn->mechlist_buf;
1608 sasl_string_list_t *_sasl_server_mechs(void)
1610 mechanism_t *listptr;
1611 sasl_string_list_t *retval = NULL, *next=NULL;
1613 if(!_sasl_server_active) return NULL;
1616 for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
1617 next = sasl_ALLOC(sizeof(sasl_string_list_t));
1619 if(!next && !retval) return NULL;
1621 next = retval->next;
1625 next = retval->next;
1630 next->d = listptr->m.plug->mech_name;
1636 next->next = retval;
1644 #define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
1645 static int is_mech(const char *t, const char *m)
1647 size_t sl = strlen(m);
1648 return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
1651 /* returns OK if it's valid */
1652 static int _sasl_checkpass(sasl_conn_t *conn,
1658 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1660 sasl_getopt_t *getopt;
1661 sasl_server_userdb_checkpass_t *checkpass_cb;
1663 const char *mlist = NULL, *mech = NULL;
1664 struct sasl_verify_password_s *v;
1665 const char *service = conn->service;
1667 if (!userlen) userlen = (unsigned) strlen(user);
1668 if (!passlen) passlen = (unsigned) strlen(pass);
1670 /* call userdb callback function, if available */
1671 result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
1672 &checkpass_cb, &context);
1673 if(result == SASL_OK && checkpass_cb) {
1674 result = checkpass_cb(conn, context, user, pass, passlen,
1675 s_conn->sparams->propctx);
1676 if(result == SASL_OK)
1680 /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1681 if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1683 getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1686 if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1688 result = SASL_NOMECH;
1691 while (*mech && result != SASL_OK) {
1692 for (v = _sasl_verify_password; v->name; v++) {
1693 if(is_mech(mech, v->name)) {
1694 result = v->verify(conn, user, pass, service,
1695 s_conn->user_realm);
1699 if (result != SASL_OK) {
1700 /* skip to next mech in list */
1701 while (*mech && !isspace((int) *mech)) mech++;
1702 while (*mech && isspace((int) *mech)) mech++;
1704 else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) {
1705 s_conn->sparams->transition(conn, pass, passlen);
1709 if (result == SASL_NOMECH) {
1710 /* no mechanism available ?!? */
1711 _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
1714 if (result != SASL_OK)
1715 sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
1717 RETURN(conn, result);
1720 /* check if a plaintext password is valid
1721 * if user is NULL, check if plaintext passwords are enabled
1723 * user -- user to query in current user_domain
1724 * userlen -- length of username, 0 = strlen(user)
1725 * pass -- plaintext password to check
1726 * passlen -- length of password, 0 = strlen(pass)
1728 * SASL_OK -- success
1729 * SASL_NOMECH -- mechanism not supported
1730 * SASL_NOVERIFY -- user found, but no verifier
1731 * SASL_NOUSER -- user not found
1733 int sasl_checkpass(sasl_conn_t *conn,
1741 if (_sasl_server_active==0) return SASL_NOTINIT;
1743 /* check if it's just a query if we are enabled */
1747 if (!conn) return SASL_BADPARAM;
1753 /* canonicalize the username */
1754 result = _sasl_canon_user(conn, user, userlen,
1755 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1757 if(result != SASL_OK) RETURN(conn, result);
1758 user = conn->oparams.user;
1760 /* Check the password */
1761 result = _sasl_checkpass(conn, user, userlen, pass, passlen);
1763 /* Do authorization */
1764 if(result == SASL_OK) {
1765 result = do_authorization((sasl_server_conn_t *)conn);
1768 RETURN(conn,result);
1771 /* check if a user exists on server
1772 * conn -- connection context (may be NULL, used to hold last error)
1773 * service -- registered name of the service using SASL (e.g. "imap")
1774 * user_realm -- permits multiple user realms on server, NULL = default
1775 * user -- NUL terminated user name
1778 * SASL_OK -- success
1779 * SASL_DISABLED -- account disabled [FIXME: currently not detected]
1780 * SASL_NOUSER -- user not found
1781 * SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
1782 * SASL_NOMECH -- no mechanisms enabled
1784 int sasl_user_exists(sasl_conn_t *conn,
1785 const char *service,
1786 const char *user_realm,
1789 int result=SASL_NOMECH;
1790 const char *mlist = NULL, *mech = NULL;
1792 sasl_getopt_t *getopt;
1793 struct sasl_verify_password_s *v;
1796 if (_sasl_server_active==0) return SASL_NOTINIT;
1797 if (!conn) return SASL_BADPARAM;
1798 if (!user || conn->type != SASL_CONN_SERVER)
1801 if(!service) service = conn->service;
1803 /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1804 if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1806 getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1809 if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1811 result = SASL_NOMECH;
1814 while (*mech && result != SASL_OK) {
1815 for (v = _sasl_verify_password; v->name; v++) {
1816 if(is_mech(mech, v->name)) {
1817 result = v->verify(conn, user, NULL, service, user_realm);
1821 if (result != SASL_OK) {
1822 /* skip to next mech in list */
1823 while (*mech && !isspace((int) *mech)) mech++;
1824 while (*mech && isspace((int) *mech)) mech++;
1828 /* Screen out the SASL_BADPARAM response
1829 * we'll get from not giving a password */
1830 if(result == SASL_BADPARAM) {
1834 if (result == SASL_NOMECH) {
1835 /* no mechanism available ?!? */
1836 _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
1837 sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
1840 RETURN(conn, result);
1843 /* check if an apop exchange is valid
1844 * (note this is an optional part of the SASL API)
1845 * if challenge is NULL, just check if APOP is enabled
1847 * challenge -- challenge which was sent to client
1848 * challen -- length of challenge, 0 = strlen(challenge)
1849 * response -- client response, "<user> <digest>" (RFC 1939)
1850 * resplen -- length of response, 0 = strlen(response)
1852 * SASL_OK -- success
1853 * SASL_BADAUTH -- authentication failed
1854 * SASL_BADPARAM -- missing challenge
1855 * SASL_BADPROT -- protocol error (e.g., response in wrong format)
1856 * SASL_NOVERIFY -- user found, but no verifier
1857 * SASL_NOMECH -- mechanism not supported
1858 * SASL_NOUSER -- user not found
1860 int sasl_checkapop(sasl_conn_t *conn,
1861 #ifdef DO_SASL_CHECKAPOP
1862 const char *challenge,
1863 unsigned challen __attribute__((unused)),
1864 const char *response,
1865 unsigned resplen __attribute__((unused)))
1867 const char *challenge __attribute__((unused)),
1868 unsigned challen __attribute__((unused)),
1869 const char *response __attribute__((unused)),
1870 unsigned resplen __attribute__((unused)))
1873 #ifdef DO_SASL_CHECKAPOP
1874 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1875 char *user, *user_end;
1876 const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
1880 if (_sasl_server_active==0)
1881 return SASL_NOTINIT;
1883 /* check if it's just a query if we are enabled */
1888 if (!conn) return SASL_BADPARAM;
1892 /* Parse out username and digest.
1894 * Per RFC 1939, response must be "<user> <digest>", where
1895 * <digest> is a 16-octet value which is sent in hexadecimal
1896 * format, using lower-case ASCII characters.
1898 user_end = strrchr(response, ' ');
1899 if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32)
1901 sasl_seterror(conn, 0, "Bad Digest");
1902 RETURN(conn,SASL_BADPROT);
1905 user_len = (size_t)(user_end - response);
1906 user = sasl_ALLOC(user_len + 1);
1907 memcpy(user, response, user_len);
1908 user[user_len] = '\0';
1910 result = prop_request(s_conn->sparams->propctx, password_request);
1911 if(result != SASL_OK)
1914 RETURN(conn, result);
1917 /* erase the plaintext password */
1918 s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx,
1919 password_request[0]);
1922 result = _sasl_canon_user(conn, user, user_len,
1923 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1927 if(result != SASL_OK) RETURN(conn, result);
1929 /* Do APOP verification */
1930 result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
1931 challenge, user_end + 1, s_conn->user_realm);
1933 /* Do authorization */
1934 if(result == SASL_OK) {
1935 result = do_authorization((sasl_server_conn_t *)conn);
1937 /* If verification failed, we don't want to encourage getprop to work */
1938 conn->oparams.user = NULL;
1939 conn->oparams.authid = NULL;
1942 RETURN(conn, result);
1943 #else /* sasl_checkapop was disabled at compile time */
1944 sasl_seterror(conn, SASL_NOLOG,
1945 "sasl_checkapop called, but was disabled at compile time");
1946 RETURN(conn, SASL_NOMECH);
1947 #endif /* DO_SASL_CHECKAPOP */
1950 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
1952 _sasl_print_mechanism (
1953 server_sasl_mechanism_t *m,
1954 sasl_info_callback_stage_t stage,
1960 if (stage == SASL_INFO_LIST_START) {
1961 printf ("List of server plugins follows\n");
1963 } else if (stage == SASL_INFO_LIST_END) {
1967 /* Process the mechanism */
1968 printf ("Plugin \"%s\" ", m->plugname);
1970 switch (m->condition) {
1972 printf ("[loaded]");
1976 printf ("[delayed]");
1980 printf ("[no users]");
1984 printf ("[unknown]");
1988 printf (", \tAPI version: %d\n", m->version);
1990 if (m->plug != NULL) {
1991 printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n",
1994 (m->plug->setpass != NULL) ? "yes" : "no"
1998 printf ("\tsecurity flags:");
2001 if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
2002 printf ("%cNO_ANONYMOUS", delimiter);
2006 if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
2007 printf ("%cNO_PLAINTEXT", delimiter);
2011 if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
2012 printf ("%cNO_ACTIVE", delimiter);
2016 if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
2017 printf ("%cNO_DICTIONARY", delimiter);
2021 if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
2022 printf ("%cFORWARD_SECRECY", delimiter);
2026 if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
2027 printf ("%cPASS_CREDENTIALS", delimiter);
2031 if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
2032 printf ("%cMUTUAL_AUTH", delimiter);
2038 printf ("\n\tfeatures:");
2041 if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
2042 printf ("%cWANT_CLIENT_FIRST", delimiter);
2046 if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
2047 printf ("%cSERVER_FIRST", delimiter);
2051 if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
2052 printf ("%cPROXY_AUTHENTICATION", delimiter);
2056 if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
2057 printf ("%cNEED_SERVER_FQDN", delimiter);
2061 /* Is this one used? */
2062 if (m->plug->features & SASL_FEAT_SERVICE) {
2063 printf ("%cSERVICE", delimiter);
2067 if (m->plug->features & SASL_FEAT_GETSECRET) {
2068 printf ("%cNEED_GETSECRET", delimiter);
2072 if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
2073 printf ("%cGSS_FRAMING", delimiter);
2077 if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
2078 printf ("%cCHANNEL_BINDING", delimiter);
2084 printf ("\n\twill be loaded from \"%s\"", m->f);
2090 /* Dump information about available server plugins (separate functions should be
2091 used for canon and auxprop plugins */
2092 int sasl_server_plugin_info (
2093 const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
2094 sasl_server_info_callback_t *info_cb,
2099 server_sasl_mechanism_t plug_data;
2101 char *mech_list = NULL;
2104 if (info_cb == NULL) {
2105 info_cb = _sasl_print_mechanism;
2108 if (mechlist != NULL) {
2109 info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
2111 if (c_mech_list == NULL) {
2112 m = mechlist->mech_list; /* m point to beginning of the list */
2115 memcpy (&plug_data, &m->m, sizeof(plug_data));
2117 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2122 mech_list = strdup(c_mech_list);
2124 cur_mech = mech_list;
2126 while (cur_mech != NULL) {
2127 p = strchr (cur_mech, ' ');
2133 m = mechlist->mech_list; /* m point to beginning of the list */
2136 if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
2137 memcpy (&plug_data, &m->m, sizeof(plug_data));
2139 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2151 info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
2156 return (SASL_NOTINIT);