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;
1030 getopt(context, NULL, "mech_list", &mlist, NULL);
1032 /* if we have a list, check the plugin against it */
1037 for (cp = mlist; *cp && !isspace((int) *cp); cp++);
1038 if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) {
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;
1220 if (_sasl_server_active==0) return SASL_NOTINIT;
1222 /* make sure mech is valid mechanism
1223 if not return appropriate error */
1224 m=mechlist->mech_list;
1225 mech_len = strlen(mech);
1227 /* check parameters */
1228 if(!conn) return SASL_BADPARAM;
1230 if (!mech || ((clientin==NULL) && (clientinlen>0)))
1233 if(serverout) *serverout = NULL;
1234 if(serveroutlen) *serveroutlen = 0;
1237 if (_sasl_is_equal_mech(mech, m->m.plug->mech_name, mech_len, &plus))
1244 sasl_seterror(conn, 0, "Couldn't find mech %s", mech);
1245 result = SASL_NOMECH;
1249 /* Make sure that we're willing to use this mech */
1250 if ((result = mech_permitted(conn, m)) != SASL_OK) {
1254 if (m->m.condition == SASL_CONTINUE) {
1255 sasl_server_plug_init_t *entry_point;
1256 void *library = NULL;
1257 sasl_server_plug_t *pluglist;
1258 int version, plugcount;
1261 /* need to load this plugin */
1262 result = _sasl_get_plugin(m->m.f,
1263 _sasl_find_verifyfile_callback(global_callbacks.callbacks),
1266 if (result == SASL_OK) {
1267 result = _sasl_locate_entry(library, "sasl_server_plug_init",
1268 (void **)&entry_point);
1271 if (result == SASL_OK) {
1272 result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION,
1273 &version, &pluglist, &plugcount);
1276 if (result == SASL_OK) {
1277 /* find the correct mechanism in this plugin */
1278 for (l = 0; l < plugcount; l++) {
1279 if (!strcasecmp(pluglist[l].mech_name,
1280 m->m.plug->mech_name)) break;
1282 if (l == plugcount) {
1283 result = SASL_NOMECH;
1286 if (result == SASL_OK) {
1287 /* check that the parameters are the same */
1288 if ((pluglist[l].max_ssf != m->m.plug->max_ssf) ||
1289 (pluglist[l].security_flags != m->m.plug->security_flags)) {
1290 _sasl_log(conn, SASL_LOG_ERR,
1291 "%s: security parameters don't match mechlist file",
1292 pluglist[l].mech_name);
1293 result = SASL_NOMECH;
1296 if (result == SASL_OK) {
1297 /* copy mechlist over */
1298 sasl_FREE((sasl_server_plug_t *) m->m.plug);
1299 m->m.plug = &pluglist[l];
1300 m->m.condition = SASL_OK;
1303 if (result != SASL_OK) {
1304 /* The library will eventually be freed, don't sweat it */
1305 RETURN(conn, result);
1309 /* We used to setup sparams HERE, but now it's done
1310 inside of mech_permitted (which is called above) */
1311 prev = &s_conn->mech_contexts;
1312 for(cur = *prev; cur; prev=&cur->next,cur=cur->next) {
1313 if(cur->mech == m) {
1315 sasl_seterror(conn, 0,
1316 "Got past mech_permitted with a disallowed mech!");
1319 /* If we find it, we need to pull cur out of the
1320 list so it won't be freed later! */
1321 (*prev)->next = cur->next;
1322 conn->context = cur->context;
1329 if(!conn->context) {
1330 /* Note that we don't hand over a new challenge */
1331 result = s_conn->mech->m.plug->mech_new(s_conn->mech->m.plug->glob_context,
1337 /* the work was already done by mech_avail! */
1341 if (result == SASL_OK) {
1343 if(s_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
1344 /* Remote sent first, but mechanism does not support it.
1345 * RFC 2222 says we fail at this point. */
1346 sasl_seterror(conn, 0,
1347 "Remote sent first but mech does not allow it.");
1348 result = SASL_BADPROT;
1350 /* Mech wants client-first, so let them have it */
1351 result = sasl_server_step(conn,
1352 clientin, clientinlen,
1353 serverout, serveroutlen);
1356 if(s_conn->mech->m.plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
1357 /* Mech wants client first anyway, so we should do that */
1360 result = SASL_CONTINUE;
1362 /* Mech wants server-first, so let them have it */
1363 result = sasl_server_step(conn,
1364 clientin, clientinlen,
1365 serverout, serveroutlen);
1371 if( result != SASL_OK
1372 && result != SASL_CONTINUE
1373 && result != SASL_INTERACT) {
1375 s_conn->mech->m.plug->mech_dispose(conn->context,
1376 s_conn->sparams->utils);
1377 conn->context = NULL;
1381 RETURN(conn,result);
1385 /* perform one step of the SASL exchange
1386 * inputlen & input -- client data
1387 * NULL on first step if no optional client step
1388 * outputlen & output -- set to the server data to transmit
1389 * to the client in the next step
1390 * (library handles freeing this)
1393 * SASL_OK -- exchange is complete.
1394 * SASL_CONTINUE -- indicates another step is necessary.
1395 * SASL_TRANS -- entry for user exists, but not for mechanism
1396 * and transition is possible
1397 * SASL_BADPARAM -- service name needed
1398 * SASL_BADPROT -- invalid input from client
1402 int sasl_server_step(sasl_conn_t *conn,
1403 const char *clientin,
1404 unsigned clientinlen,
1405 const char **serverout,
1406 unsigned *serveroutlen)
1409 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */
1411 /* check parameters */
1412 if (_sasl_server_active==0) return SASL_NOTINIT;
1413 if (!conn) return SASL_BADPARAM;
1414 if ((clientin==NULL) && (clientinlen>0))
1417 /* If we've already done the last send, return! */
1418 if(s_conn->sent_last == 1) {
1422 /* Don't do another step if the plugin told us that we're done */
1423 if (conn->oparams.doneflag) {
1424 _sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag");
1428 if(serverout) *serverout = NULL;
1429 if(serveroutlen) *serveroutlen = 0;
1431 ret = s_conn->mech->m.plug->mech_step(conn->context,
1439 if (ret == SASL_OK) {
1440 ret = do_authorization(s_conn);
1443 if (ret == SASL_OK) {
1444 /* if we're done, we need to watch out for the following:
1445 * 1. the mech does server-send-last
1446 * 2. the protocol does not
1448 * in this case, return SASL_CONTINUE and remember we are done.
1450 if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) {
1451 s_conn->sent_last = 1;
1452 ret = SASL_CONTINUE;
1454 if(!conn->oparams.maxoutbuf) {
1455 conn->oparams.maxoutbuf = conn->props.maxbufsize;
1458 /* Validate channel bindings */
1459 switch (conn->oparams.cbindingdisp) {
1460 case SASL_CB_DISP_NONE:
1461 if (SASL_CB_CRITICAL(s_conn->sparams)) {
1462 sasl_seterror(conn, 0,
1463 "server requires channel binding but client provided none");
1464 ret = SASL_BADBINDING;
1467 case SASL_CB_DISP_WANT:
1468 if (SASL_CB_PRESENT(s_conn->sparams)) {
1469 sasl_seterror(conn, 0,
1470 "client incorrectly assumed server had no channel binding");
1474 case SASL_CB_DISP_USED:
1475 if (!SASL_CB_PRESENT(s_conn->sparams)) {
1476 sasl_seterror(conn, 0,
1477 "client provided channel binding but server had none");
1478 ret = SASL_BADBINDING;
1479 } else if (strcmp(conn->oparams.cbindingname,
1480 s_conn->sparams->cbinding->name) != 0) {
1481 sasl_seterror(conn, 0,
1482 "client channel binding %s does not match server %s",
1483 conn->oparams.cbindingname, s_conn->sparams->cbinding->name);
1484 ret = SASL_BADBINDING;
1489 if (ret == SASL_OK &&
1490 (conn->oparams.user == NULL || conn->oparams.authid == NULL)) {
1491 sasl_seterror(conn, 0,
1492 "mech did not call canon_user for both authzid " \
1499 && ret != SASL_CONTINUE
1500 && ret != SASL_INTERACT) {
1502 s_conn->mech->m.plug->mech_dispose(conn->context,
1503 s_conn->sparams->utils);
1504 conn->context = NULL;
1511 /* returns the length of all the mechanisms
1515 static unsigned mech_names_len()
1517 mechanism_t *listptr;
1518 unsigned result = 0;
1520 for (listptr = mechlist->mech_list;
1522 listptr = listptr->next)
1523 result += (unsigned) strlen(listptr->m.plug->mech_name);
1528 /* This returns a list of mechanisms in a NUL-terminated string
1530 * The default behavior is to seperate with spaces if sep==NULL
1532 int _sasl_server_listmech(sasl_conn_t *conn,
1533 const char *user __attribute__((unused)),
1537 const char **result,
1542 mechanism_t *listptr;
1547 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */
1549 /* if there hasn't been a sasl_sever_init() fail */
1550 if (_sasl_server_active==0) return SASL_NOTINIT;
1551 if (!conn) return SASL_BADPARAM;
1552 if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
1568 if (! mechlist || mechlist->mech_length <= 0)
1569 INTERROR(conn, SASL_NOMECH);
1571 resultlen = (prefix ? strlen(prefix) : 0)
1572 + (strlen(mysep) * (mechlist->mech_length - 1) * 2)
1573 + (mech_names_len() * 2) /* including -PLUS variant */
1574 + (mechlist->mech_length * (sizeof("-PLUS") - 1))
1575 + (suffix ? strlen(suffix) : 0)
1577 ret = _buf_alloc(&conn->mechlist_buf,
1578 &conn->mechlist_buf_len, resultlen);
1579 if(ret != SASL_OK) MEMERROR(conn);
1582 strcpy (conn->mechlist_buf,prefix);
1584 *(conn->mechlist_buf) = '\0';
1586 listptr = mechlist->mech_list;
1590 for (lup = 0; lup < mechlist->mech_length; lup++) {
1591 /* currently, we don't use the "user" parameter for anything */
1592 if (mech_permitted(conn, listptr) == SASL_OK) {
1594 * If the server would never succeed in the authentication of
1595 * he non-PLUS-variant due to policy reasons, it MUST advertise
1596 * only the PLUS-variant.
1598 if (!SASL_CB_PRESENT(s_conn->sparams) ||
1599 !SASL_CB_CRITICAL(s_conn->sparams)) {
1603 strcat(conn->mechlist_buf, mysep);
1606 strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1609 * If the server cannot support channel binding, it SHOULD
1610 * advertise only the non-PLUS-variant. Here, supporting channel
1611 * binding means the underlying SASL mechanism supports it and
1612 * the application has set some channel binding data.
1614 if ((listptr->m.plug->features & SASL_FEAT_CHANNEL_BINDING) &&
1615 SASL_CB_PRESENT(s_conn->sparams)) {
1619 strcat(conn->mechlist_buf, mysep);
1622 strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1623 strcat(conn->mechlist_buf, "-PLUS");
1627 listptr = listptr->next;
1631 strcat(conn->mechlist_buf,suffix);
1634 *plen = (unsigned) strlen(conn->mechlist_buf);
1636 *result = conn->mechlist_buf;
1641 sasl_string_list_t *_sasl_server_mechs(void)
1643 mechanism_t *listptr;
1644 sasl_string_list_t *retval = NULL, *next=NULL;
1646 if(!_sasl_server_active) return NULL;
1649 for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
1650 next = sasl_ALLOC(sizeof(sasl_string_list_t));
1652 if(!next && !retval) return NULL;
1654 next = retval->next;
1658 next = retval->next;
1663 next->d = listptr->m.plug->mech_name;
1669 next->next = retval;
1677 #define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
1678 static int is_mech(const char *t, const char *m)
1680 size_t sl = strlen(m);
1681 return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
1684 /* returns OK if it's valid */
1685 static int _sasl_checkpass(sasl_conn_t *conn,
1691 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1693 sasl_getopt_t *getopt;
1694 sasl_server_userdb_checkpass_t *checkpass_cb;
1696 const char *mlist = NULL, *mech = NULL;
1697 struct sasl_verify_password_s *v;
1698 const char *service = conn->service;
1700 if (!userlen) userlen = (unsigned) strlen(user);
1701 if (!passlen) passlen = (unsigned) strlen(pass);
1703 /* call userdb callback function, if available */
1704 result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
1705 &checkpass_cb, &context);
1706 if(result == SASL_OK && checkpass_cb) {
1707 result = checkpass_cb(conn, context, user, pass, passlen,
1708 s_conn->sparams->propctx);
1709 if(result == SASL_OK)
1713 /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1714 if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1716 getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1719 if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1721 result = SASL_NOMECH;
1724 while (*mech && result != SASL_OK) {
1725 for (v = _sasl_verify_password; v->name; v++) {
1726 if(is_mech(mech, v->name)) {
1727 result = v->verify(conn, user, pass, service,
1728 s_conn->user_realm);
1732 if (result != SASL_OK) {
1733 /* skip to next mech in list */
1734 while (*mech && !isspace((int) *mech)) mech++;
1735 while (*mech && isspace((int) *mech)) mech++;
1737 else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) {
1738 s_conn->sparams->transition(conn, pass, passlen);
1742 if (result == SASL_NOMECH) {
1743 /* no mechanism available ?!? */
1744 _sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
1747 if (result != SASL_OK)
1748 sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
1750 RETURN(conn, result);
1753 /* check if a plaintext password is valid
1754 * if user is NULL, check if plaintext passwords are enabled
1756 * user -- user to query in current user_domain
1757 * userlen -- length of username, 0 = strlen(user)
1758 * pass -- plaintext password to check
1759 * passlen -- length of password, 0 = strlen(pass)
1761 * SASL_OK -- success
1762 * SASL_NOMECH -- mechanism not supported
1763 * SASL_NOVERIFY -- user found, but no verifier
1764 * SASL_NOUSER -- user not found
1766 int sasl_checkpass(sasl_conn_t *conn,
1774 if (_sasl_server_active==0) return SASL_NOTINIT;
1776 /* check if it's just a query if we are enabled */
1780 if (!conn) return SASL_BADPARAM;
1786 /* canonicalize the username */
1787 result = _sasl_canon_user(conn, user, userlen,
1788 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1790 if(result != SASL_OK) RETURN(conn, result);
1791 user = conn->oparams.user;
1793 /* Check the password */
1794 result = _sasl_checkpass(conn, user, userlen, pass, passlen);
1796 /* Do authorization */
1797 if(result == SASL_OK) {
1798 result = do_authorization((sasl_server_conn_t *)conn);
1801 RETURN(conn,result);
1804 /* check if a user exists on server
1805 * conn -- connection context (may be NULL, used to hold last error)
1806 * service -- registered name of the service using SASL (e.g. "imap")
1807 * user_realm -- permits multiple user realms on server, NULL = default
1808 * user -- NUL terminated user name
1811 * SASL_OK -- success
1812 * SASL_DISABLED -- account disabled [FIXME: currently not detected]
1813 * SASL_NOUSER -- user not found
1814 * SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
1815 * SASL_NOMECH -- no mechanisms enabled
1817 int sasl_user_exists(sasl_conn_t *conn,
1818 const char *service,
1819 const char *user_realm,
1822 int result=SASL_NOMECH;
1823 const char *mlist = NULL, *mech = NULL;
1825 sasl_getopt_t *getopt;
1826 struct sasl_verify_password_s *v;
1829 if (_sasl_server_active==0) return SASL_NOTINIT;
1830 if (!conn) return SASL_BADPARAM;
1831 if (!user || conn->type != SASL_CONN_SERVER)
1834 if(!service) service = conn->service;
1836 /* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1837 if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1839 getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1842 if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1844 result = SASL_NOMECH;
1847 while (*mech && result != SASL_OK) {
1848 for (v = _sasl_verify_password; v->name; v++) {
1849 if(is_mech(mech, v->name)) {
1850 result = v->verify(conn, user, NULL, service, user_realm);
1854 if (result != SASL_OK) {
1855 /* skip to next mech in list */
1856 while (*mech && !isspace((int) *mech)) mech++;
1857 while (*mech && isspace((int) *mech)) mech++;
1861 /* Screen out the SASL_BADPARAM response
1862 * we'll get from not giving a password */
1863 if(result == SASL_BADPARAM) {
1867 if (result == SASL_NOMECH) {
1868 /* no mechanism available ?!? */
1869 _sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
1870 sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
1873 RETURN(conn, result);
1876 /* check if an apop exchange is valid
1877 * (note this is an optional part of the SASL API)
1878 * if challenge is NULL, just check if APOP is enabled
1880 * challenge -- challenge which was sent to client
1881 * challen -- length of challenge, 0 = strlen(challenge)
1882 * response -- client response, "<user> <digest>" (RFC 1939)
1883 * resplen -- length of response, 0 = strlen(response)
1885 * SASL_OK -- success
1886 * SASL_BADAUTH -- authentication failed
1887 * SASL_BADPARAM -- missing challenge
1888 * SASL_BADPROT -- protocol error (e.g., response in wrong format)
1889 * SASL_NOVERIFY -- user found, but no verifier
1890 * SASL_NOMECH -- mechanism not supported
1891 * SASL_NOUSER -- user not found
1893 int sasl_checkapop(sasl_conn_t *conn,
1894 #ifdef DO_SASL_CHECKAPOP
1895 const char *challenge,
1896 unsigned challen __attribute__((unused)),
1897 const char *response,
1898 unsigned resplen __attribute__((unused)))
1900 const char *challenge __attribute__((unused)),
1901 unsigned challen __attribute__((unused)),
1902 const char *response __attribute__((unused)),
1903 unsigned resplen __attribute__((unused)))
1906 #ifdef DO_SASL_CHECKAPOP
1907 sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1908 char *user, *user_end;
1909 const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
1913 if (_sasl_server_active==0)
1914 return SASL_NOTINIT;
1916 /* check if it's just a query if we are enabled */
1921 if (!conn) return SASL_BADPARAM;
1925 /* Parse out username and digest.
1927 * Per RFC 1939, response must be "<user> <digest>", where
1928 * <digest> is a 16-octet value which is sent in hexadecimal
1929 * format, using lower-case ASCII characters.
1931 user_end = strrchr(response, ' ');
1932 if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32)
1934 sasl_seterror(conn, 0, "Bad Digest");
1935 RETURN(conn,SASL_BADPROT);
1938 user_len = (size_t)(user_end - response);
1939 user = sasl_ALLOC(user_len + 1);
1940 memcpy(user, response, user_len);
1941 user[user_len] = '\0';
1943 result = prop_request(s_conn->sparams->propctx, password_request);
1944 if(result != SASL_OK)
1947 RETURN(conn, result);
1950 /* erase the plaintext password */
1951 s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx,
1952 password_request[0]);
1955 result = _sasl_canon_user(conn, user, user_len,
1956 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1960 if(result != SASL_OK) RETURN(conn, result);
1962 /* Do APOP verification */
1963 result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
1964 challenge, user_end + 1, s_conn->user_realm);
1966 /* Do authorization */
1967 if(result == SASL_OK) {
1968 result = do_authorization((sasl_server_conn_t *)conn);
1970 /* If verification failed, we don't want to encourage getprop to work */
1971 conn->oparams.user = NULL;
1972 conn->oparams.authid = NULL;
1975 RETURN(conn, result);
1976 #else /* sasl_checkapop was disabled at compile time */
1977 sasl_seterror(conn, SASL_NOLOG,
1978 "sasl_checkapop called, but was disabled at compile time");
1979 RETURN(conn, SASL_NOMECH);
1980 #endif /* DO_SASL_CHECKAPOP */
1983 /* It would be nice if we can show other information like Author, Company, Year, plugin version */
1985 _sasl_print_mechanism (
1986 server_sasl_mechanism_t *m,
1987 sasl_info_callback_stage_t stage,
1993 if (stage == SASL_INFO_LIST_START) {
1994 printf ("List of server plugins follows\n");
1996 } else if (stage == SASL_INFO_LIST_END) {
2000 /* Process the mechanism */
2001 printf ("Plugin \"%s\" ", m->plugname);
2003 switch (m->condition) {
2005 printf ("[loaded]");
2009 printf ("[delayed]");
2013 printf ("[no users]");
2017 printf ("[unknown]");
2021 printf (", \tAPI version: %d\n", m->version);
2023 if (m->plug != NULL) {
2024 printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n",
2027 (m->plug->setpass != NULL) ? "yes" : "no"
2031 printf ("\tsecurity flags:");
2034 if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
2035 printf ("%cNO_ANONYMOUS", delimiter);
2039 if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
2040 printf ("%cNO_PLAINTEXT", delimiter);
2044 if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
2045 printf ("%cNO_ACTIVE", delimiter);
2049 if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
2050 printf ("%cNO_DICTIONARY", delimiter);
2054 if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
2055 printf ("%cFORWARD_SECRECY", delimiter);
2059 if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
2060 printf ("%cPASS_CREDENTIALS", delimiter);
2064 if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
2065 printf ("%cMUTUAL_AUTH", delimiter);
2071 printf ("\n\tfeatures:");
2074 if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
2075 printf ("%cWANT_CLIENT_FIRST", delimiter);
2079 if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
2080 printf ("%cSERVER_FIRST", delimiter);
2084 if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
2085 printf ("%cPROXY_AUTHENTICATION", delimiter);
2089 if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
2090 printf ("%cNEED_SERVER_FQDN", delimiter);
2094 /* Is this one used? */
2095 if (m->plug->features & SASL_FEAT_SERVICE) {
2096 printf ("%cSERVICE", delimiter);
2100 if (m->plug->features & SASL_FEAT_GETSECRET) {
2101 printf ("%cNEED_GETSECRET", delimiter);
2105 if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
2106 printf ("%cGSS_FRAMING", delimiter);
2110 if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
2111 printf ("%cCHANNEL_BINDING", delimiter);
2117 printf ("\n\twill be loaded from \"%s\"", m->f);
2123 /* Dump information about available server plugins (separate functions should be
2124 used for canon and auxprop plugins */
2125 int sasl_server_plugin_info (
2126 const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
2127 sasl_server_info_callback_t *info_cb,
2132 server_sasl_mechanism_t plug_data;
2134 char *mech_list = NULL;
2137 if (info_cb == NULL) {
2138 info_cb = _sasl_print_mechanism;
2141 if (mechlist != NULL) {
2142 info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
2144 if (c_mech_list == NULL) {
2145 m = mechlist->mech_list; /* m point to beginning of the list */
2148 memcpy (&plug_data, &m->m, sizeof(plug_data));
2150 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2155 mech_list = strdup(c_mech_list);
2157 cur_mech = mech_list;
2159 while (cur_mech != NULL) {
2160 p = strchr (cur_mech, ' ');
2166 m = mechlist->mech_list; /* m point to beginning of the list */
2169 if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
2170 memcpy (&plug_data, &m->m, sizeof(plug_data));
2172 info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2184 info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
2189 return (SASL_NOTINIT);