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;
1218
if (_sasl_server_active==0) return SASL_NOTINIT;
1220
/* make sure mech is valid mechanism
1221
if not return appropriate error */
1222
m=mechlist->mech_list;
1224
/* check parameters */
1225
if(!conn) return SASL_BADPARAM;
1227
if (!mech || ((clientin==NULL) && (clientinlen>0)))
1230
if(serverout) *serverout = NULL;
1231
if(serveroutlen) *serveroutlen = 0;
1235
if ( strcasecmp(mech, m->m.plug->mech_name)==0)
1243
sasl_seterror(conn, 0, "Couldn't find mech %s", mech);
1244
result = SASL_NOMECH;
1248
/* Make sure that we're willing to use this mech */
1249
if ((result = mech_permitted(conn, m)) != SASL_OK) {
1253
if (m->m.condition == SASL_CONTINUE) {
1254
sasl_server_plug_init_t *entry_point;
1255
void *library = NULL;
1256
sasl_server_plug_t *pluglist;
1257
int version, plugcount;
1260
/* need to load this plugin */
1261
result = _sasl_get_plugin(m->m.f,
1262
_sasl_find_verifyfile_callback(global_callbacks.callbacks),
1265
if (result == SASL_OK) {
1266
result = _sasl_locate_entry(library, "sasl_server_plug_init",
1267
(void **)&entry_point);
1270
if (result == SASL_OK) {
1271
result = entry_point(mechlist->utils, SASL_SERVER_PLUG_VERSION,
1272
&version, &pluglist, &plugcount);
1275
if (result == SASL_OK) {
1276
/* find the correct mechanism in this plugin */
1277
for (l = 0; l < plugcount; l++) {
1278
if (!strcasecmp(pluglist[l].mech_name,
1279
m->m.plug->mech_name)) break;
1281
if (l == plugcount) {
1282
result = SASL_NOMECH;
1285
if (result == SASL_OK) {
1286
/* check that the parameters are the same */
1287
if ((pluglist[l].max_ssf != m->m.plug->max_ssf) ||
1288
(pluglist[l].security_flags != m->m.plug->security_flags)) {
1289
_sasl_log(conn, SASL_LOG_ERR,
1290
"%s: security parameters don't match mechlist file",
1291
pluglist[l].mech_name);
1292
result = SASL_NOMECH;
1295
if (result == SASL_OK) {
1296
/* copy mechlist over */
1297
sasl_FREE((sasl_server_plug_t *) m->m.plug);
1298
m->m.plug = &pluglist[l];
1299
m->m.condition = SASL_OK;
1302
if (result != SASL_OK) {
1303
/* The library will eventually be freed, don't sweat it */
1304
RETURN(conn, result);
1308
/* We used to setup sparams HERE, but now it's done
1309
inside of mech_permitted (which is called above) */
1310
prev = &s_conn->mech_contexts;
1311
for(cur = *prev; cur; prev=&cur->next,cur=cur->next) {
1312
if(cur->mech == m) {
1314
sasl_seterror(conn, 0,
1315
"Got past mech_permitted with a disallowed mech!");
1318
/* If we find it, we need to pull cur out of the
1319
list so it won't be freed later! */
1320
(*prev)->next = cur->next;
1321
conn->context = cur->context;
1328
if(!conn->context) {
1329
/* Note that we don't hand over a new challenge */
1330
result = s_conn->mech->m.plug->mech_new(s_conn->mech->m.plug->glob_context,
1336
/* the work was already done by mech_avail! */
1340
if (result == SASL_OK) {
1342
if(s_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
1343
/* Remote sent first, but mechanism does not support it.
1344
* RFC 2222 says we fail at this point. */
1345
sasl_seterror(conn, 0,
1346
"Remote sent first but mech does not allow it.");
1347
result = SASL_BADPROT;
1349
/* Mech wants client-first, so let them have it */
1350
result = sasl_server_step(conn,
1351
clientin, clientinlen,
1352
serverout, serveroutlen);
1355
if(s_conn->mech->m.plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
1356
/* Mech wants client first anyway, so we should do that */
1359
result = SASL_CONTINUE;
1361
/* Mech wants server-first, so let them have it */
1362
result = sasl_server_step(conn,
1363
clientin, clientinlen,
1364
serverout, serveroutlen);
1370
if( result != SASL_OK
1371
&& result != SASL_CONTINUE
1372
&& result != SASL_INTERACT) {
1374
s_conn->mech->m.plug->mech_dispose(conn->context,
1375
s_conn->sparams->utils);
1376
conn->context = NULL;
1380
RETURN(conn,result);
1384
/* perform one step of the SASL exchange
1385
* inputlen & input -- client data
1386
* NULL on first step if no optional client step
1387
* outputlen & output -- set to the server data to transmit
1388
* to the client in the next step
1389
* (library handles freeing this)
1392
* SASL_OK -- exchange is complete.
1393
* SASL_CONTINUE -- indicates another step is necessary.
1394
* SASL_TRANS -- entry for user exists, but not for mechanism
1395
* and transition is possible
1396
* SASL_BADPARAM -- service name needed
1397
* SASL_BADPROT -- invalid input from client
1401
int sasl_server_step(sasl_conn_t *conn,
1402
const char *clientin,
1403
unsigned clientinlen,
1404
const char **serverout,
1405
unsigned *serveroutlen)
1408
sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn; /* cast */
1410
/* check parameters */
1411
if (_sasl_server_active==0) return SASL_NOTINIT;
1412
if (!conn) return SASL_BADPARAM;
1413
if ((clientin==NULL) && (clientinlen>0))
1416
/* If we've already done the last send, return! */
1417
if(s_conn->sent_last == 1) {
1421
/* Don't do another step if the plugin told us that we're done */
1422
if (conn->oparams.doneflag) {
1423
_sasl_log(conn, SASL_LOG_ERR, "attempting server step after doneflag");
1427
if(serverout) *serverout = NULL;
1428
if(serveroutlen) *serveroutlen = 0;
1430
ret = s_conn->mech->m.plug->mech_step(conn->context,
1438
if (ret == SASL_OK) {
1439
ret = do_authorization(s_conn);
1442
if (ret == SASL_OK) {
1443
/* if we're done, we need to watch out for the following:
1444
* 1. the mech does server-send-last
1445
* 2. the protocol does not
1447
* in this case, return SASL_CONTINUE and remember we are done.
1449
if(*serverout && !(conn->flags & SASL_SUCCESS_DATA)) {
1450
s_conn->sent_last = 1;
1451
ret = SASL_CONTINUE;
1453
if(!conn->oparams.maxoutbuf) {
1454
conn->oparams.maxoutbuf = conn->props.maxbufsize;
1457
if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
1458
sasl_seterror(conn, 0,
1459
"mech did not call canon_user for both authzid " \
1466
&& ret != SASL_CONTINUE
1467
&& ret != SASL_INTERACT) {
1469
s_conn->mech->m.plug->mech_dispose(conn->context,
1470
s_conn->sparams->utils);
1471
conn->context = NULL;
1478
/* returns the length of all the mechanisms
1482
static unsigned mech_names_len()
1484
mechanism_t *listptr;
1485
unsigned result = 0;
1487
for (listptr = mechlist->mech_list;
1489
listptr = listptr->next)
1490
result += (unsigned) strlen(listptr->m.plug->mech_name);
1495
/* This returns a list of mechanisms in a NUL-terminated string
1497
* The default behavior is to seperate with spaces if sep==NULL
1499
int _sasl_server_listmech(sasl_conn_t *conn,
1500
const char *user __attribute__((unused)),
1504
const char **result,
1509
mechanism_t *listptr;
1515
/* if there hasn't been a sasl_sever_init() fail */
1516
if (_sasl_server_active==0) return SASL_NOTINIT;
1517
if (!conn) return SASL_BADPARAM;
1518
if (conn->type != SASL_CONN_SERVER) PARAMERROR(conn);
1534
if (! mechlist || mechlist->mech_length <= 0)
1535
INTERROR(conn, SASL_NOMECH);
1537
resultlen = (prefix ? strlen(prefix) : 0)
1538
+ (strlen(mysep) * (mechlist->mech_length - 1))
1540
+ (suffix ? strlen(suffix) : 0)
1542
ret = _buf_alloc(&conn->mechlist_buf,
1543
&conn->mechlist_buf_len, resultlen);
1544
if(ret != SASL_OK) MEMERROR(conn);
1547
strcpy (conn->mechlist_buf,prefix);
1549
*(conn->mechlist_buf) = '\0';
1551
listptr = mechlist->mech_list;
1555
for (lup = 0; lup < mechlist->mech_length; lup++) {
1556
/* currently, we don't use the "user" parameter for anything */
1557
if (mech_permitted(conn, listptr) == SASL_OK) {
1561
/* print separator */
1563
strcat(conn->mechlist_buf, mysep);
1568
/* now print the mechanism name */
1569
strcat(conn->mechlist_buf, listptr->m.plug->mech_name);
1572
listptr = listptr->next;
1576
strcat(conn->mechlist_buf,suffix);
1579
*plen = (unsigned) strlen(conn->mechlist_buf);
1581
*result = conn->mechlist_buf;
1586
sasl_string_list_t *_sasl_server_mechs(void)
1588
mechanism_t *listptr;
1589
sasl_string_list_t *retval = NULL, *next=NULL;
1591
if(!_sasl_server_active) return NULL;
1594
for (listptr = mechlist->mech_list; listptr; listptr = listptr->next) {
1595
next = sasl_ALLOC(sizeof(sasl_string_list_t));
1597
if(!next && !retval) return NULL;
1599
next = retval->next;
1603
next = retval->next;
1608
next->d = listptr->m.plug->mech_name;
1614
next->next = retval;
1622
#define EOSTR(s,n) (((s)[n] == '\0') || ((s)[n] == ' ') || ((s)[n] == '\t'))
1623
static int is_mech(const char *t, const char *m)
1625
size_t sl = strlen(m);
1626
return ((!strncasecmp(m, t, sl)) && EOSTR(t, sl));
1629
/* returns OK if it's valid */
1630
static int _sasl_checkpass(sasl_conn_t *conn,
1636
sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1638
sasl_getopt_t *getopt;
1639
sasl_server_userdb_checkpass_t *checkpass_cb;
1641
const char *mlist = NULL, *mech = NULL;
1642
struct sasl_verify_password_s *v;
1643
const char *service = conn->service;
1645
if (!userlen) userlen = (unsigned) strlen(user);
1646
if (!passlen) passlen = (unsigned) strlen(pass);
1648
/* call userdb callback function, if available */
1649
result = _sasl_getcallback(conn, SASL_CB_SERVER_USERDB_CHECKPASS,
1650
&checkpass_cb, &context);
1651
if(result == SASL_OK && checkpass_cb) {
1652
result = checkpass_cb(conn, context, user, pass, passlen,
1653
s_conn->sparams->propctx);
1654
if(result == SASL_OK)
1658
/* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1659
if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1661
getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1664
if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1666
result = SASL_NOMECH;
1669
while (*mech && result != SASL_OK) {
1670
for (v = _sasl_verify_password; v->name; v++) {
1671
if(is_mech(mech, v->name)) {
1672
result = v->verify(conn, user, pass, service,
1673
s_conn->user_realm);
1677
if (result != SASL_OK) {
1678
/* skip to next mech in list */
1679
while (*mech && !isspace((int) *mech)) mech++;
1680
while (*mech && isspace((int) *mech)) mech++;
1682
else if (!is_mech(mech, "auxprop") && s_conn->sparams->transition) {
1683
s_conn->sparams->transition(conn, pass, passlen);
1687
if (result == SASL_NOMECH) {
1688
/* no mechanism available ?!? */
1689
_sasl_log(conn, SASL_LOG_ERR, "unknown password verifier %s", mech);
1692
if (result != SASL_OK)
1693
sasl_seterror(conn, SASL_NOLOG, "checkpass failed");
1695
RETURN(conn, result);
1698
/* check if a plaintext password is valid
1699
* if user is NULL, check if plaintext passwords are enabled
1701
* user -- user to query in current user_domain
1702
* userlen -- length of username, 0 = strlen(user)
1703
* pass -- plaintext password to check
1704
* passlen -- length of password, 0 = strlen(pass)
1706
* SASL_OK -- success
1707
* SASL_NOMECH -- mechanism not supported
1708
* SASL_NOVERIFY -- user found, but no verifier
1709
* SASL_NOUSER -- user not found
1711
int sasl_checkpass(sasl_conn_t *conn,
1719
if (_sasl_server_active==0) return SASL_NOTINIT;
1721
/* check if it's just a query if we are enabled */
1725
if (!conn) return SASL_BADPARAM;
1731
/* canonicalize the username */
1732
result = _sasl_canon_user(conn, user, userlen,
1733
SASL_CU_AUTHID | SASL_CU_AUTHZID,
1735
if(result != SASL_OK) RETURN(conn, result);
1736
user = conn->oparams.user;
1738
/* Check the password */
1739
result = _sasl_checkpass(conn, user, userlen, pass, passlen);
1741
/* Do authorization */
1742
if(result == SASL_OK) {
1743
result = do_authorization((sasl_server_conn_t *)conn);
1746
RETURN(conn,result);
1749
/* check if a user exists on server
1750
* conn -- connection context (may be NULL, used to hold last error)
1751
* service -- registered name of the service using SASL (e.g. "imap")
1752
* user_realm -- permits multiple user realms on server, NULL = default
1753
* user -- NUL terminated user name
1756
* SASL_OK -- success
1757
* SASL_DISABLED -- account disabled [FIXME: currently not detected]
1758
* SASL_NOUSER -- user not found
1759
* SASL_NOVERIFY -- user found, but no usable mechanism [FIXME: not supported]
1760
* SASL_NOMECH -- no mechanisms enabled
1762
int sasl_user_exists(sasl_conn_t *conn,
1763
const char *service,
1764
const char *user_realm,
1767
int result=SASL_NOMECH;
1768
const char *mlist = NULL, *mech = NULL;
1770
sasl_getopt_t *getopt;
1771
struct sasl_verify_password_s *v;
1774
if (_sasl_server_active==0) return SASL_NOTINIT;
1775
if (!conn) return SASL_BADPARAM;
1776
if (!user || conn->type != SASL_CONN_SERVER)
1779
if(!service) service = conn->service;
1781
/* figure out how to check (i.e. auxprop or saslauthd or pwcheck) */
1782
if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context)
1784
getopt(context, NULL, "pwcheck_method", &mlist, NULL);
1787
if(!mlist) mlist = DEFAULT_CHECKPASS_MECH;
1789
result = SASL_NOMECH;
1792
while (*mech && result != SASL_OK) {
1793
for (v = _sasl_verify_password; v->name; v++) {
1794
if(is_mech(mech, v->name)) {
1795
result = v->verify(conn, user, NULL, service, user_realm);
1799
if (result != SASL_OK) {
1800
/* skip to next mech in list */
1801
while (*mech && !isspace((int) *mech)) mech++;
1802
while (*mech && isspace((int) *mech)) mech++;
1806
/* Screen out the SASL_BADPARAM response
1807
* we'll get from not giving a password */
1808
if(result == SASL_BADPARAM) {
1812
if (result == SASL_NOMECH) {
1813
/* no mechanism available ?!? */
1814
_sasl_log(conn, SASL_LOG_ERR, "no plaintext password verifier?");
1815
sasl_seterror(conn, SASL_NOLOG, "no plaintext password verifier?");
1818
RETURN(conn, result);
1821
/* check if an apop exchange is valid
1822
* (note this is an optional part of the SASL API)
1823
* if challenge is NULL, just check if APOP is enabled
1825
* challenge -- challenge which was sent to client
1826
* challen -- length of challenge, 0 = strlen(challenge)
1827
* response -- client response, "<user> <digest>" (RFC 1939)
1828
* resplen -- length of response, 0 = strlen(response)
1830
* SASL_OK -- success
1831
* SASL_BADAUTH -- authentication failed
1832
* SASL_BADPARAM -- missing challenge
1833
* SASL_BADPROT -- protocol error (e.g., response in wrong format)
1834
* SASL_NOVERIFY -- user found, but no verifier
1835
* SASL_NOMECH -- mechanism not supported
1836
* SASL_NOUSER -- user not found
1838
int sasl_checkapop(sasl_conn_t *conn,
1839
#ifdef DO_SASL_CHECKAPOP
1840
const char *challenge,
1841
unsigned challen __attribute__((unused)),
1842
const char *response,
1843
unsigned resplen __attribute__((unused)))
1845
const char *challenge __attribute__((unused)),
1846
unsigned challen __attribute__((unused)),
1847
const char *response __attribute__((unused)),
1848
unsigned resplen __attribute__((unused)))
1851
#ifdef DO_SASL_CHECKAPOP
1852
sasl_server_conn_t *s_conn = (sasl_server_conn_t *) conn;
1853
char *user, *user_end;
1854
const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
1858
if (_sasl_server_active==0)
1859
return SASL_NOTINIT;
1861
/* check if it's just a query if we are enabled */
1866
if (!conn) return SASL_BADPARAM;
1870
/* Parse out username and digest.
1872
* Per RFC 1939, response must be "<user> <digest>", where
1873
* <digest> is a 16-octet value which is sent in hexadecimal
1874
* format, using lower-case ASCII characters.
1876
user_end = strrchr(response, ' ');
1877
if (!user_end || strspn(user_end + 1, "0123456789abcdef") != 32)
1879
sasl_seterror(conn, 0, "Bad Digest");
1880
RETURN(conn,SASL_BADPROT);
1883
user_len = (size_t)(user_end - response);
1884
user = sasl_ALLOC(user_len + 1);
1885
memcpy(user, response, user_len);
1886
user[user_len] = '\0';
1888
result = prop_request(s_conn->sparams->propctx, password_request);
1889
if(result != SASL_OK)
1892
RETURN(conn, result);
1895
/* erase the plaintext password */
1896
s_conn->sparams->utils->prop_erase(s_conn->sparams->propctx,
1897
password_request[0]);
1900
result = _sasl_canon_user(conn, user, user_len,
1901
SASL_CU_AUTHID | SASL_CU_AUTHZID,
1905
if(result != SASL_OK) RETURN(conn, result);
1907
/* Do APOP verification */
1908
result = _sasl_auxprop_verify_apop(conn, conn->oparams.authid,
1909
challenge, user_end + 1, s_conn->user_realm);
1911
/* Do authorization */
1912
if(result == SASL_OK) {
1913
result = do_authorization((sasl_server_conn_t *)conn);
1915
/* If verification failed, we don't want to encourage getprop to work */
1916
conn->oparams.user = NULL;
1917
conn->oparams.authid = NULL;
1920
RETURN(conn, result);
1921
#else /* sasl_checkapop was disabled at compile time */
1922
sasl_seterror(conn, SASL_NOLOG,
1923
"sasl_checkapop called, but was disabled at compile time");
1924
RETURN(conn, SASL_NOMECH);
1925
#endif /* DO_SASL_CHECKAPOP */
1928
/* It would be nice if we can show other information like Author, Company, Year, plugin version */
1930
_sasl_print_mechanism (
1931
server_sasl_mechanism_t *m,
1932
sasl_info_callback_stage_t stage,
1938
if (stage == SASL_INFO_LIST_START) {
1939
printf ("List of server plugins follows\n");
1941
} else if (stage == SASL_INFO_LIST_END) {
1945
/* Process the mechanism */
1946
printf ("Plugin \"%s\" ", m->plugname);
1948
switch (m->condition) {
1950
printf ("[loaded]");
1954
printf ("[delayed]");
1958
printf ("[no users]");
1962
printf ("[unknown]");
1966
printf (", \tAPI version: %d\n", m->version);
1968
if (m->plug != NULL) {
1969
printf ("\tSASL mechanism: %s, best SSF: %d, supports setpass: %s\n",
1972
(m->plug->setpass != NULL) ? "yes" : "no"
1976
printf ("\tsecurity flags:");
1979
if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
1980
printf ("%cNO_ANONYMOUS", delimiter);
1984
if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
1985
printf ("%cNO_PLAINTEXT", delimiter);
1989
if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
1990
printf ("%cNO_ACTIVE", delimiter);
1994
if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
1995
printf ("%cNO_DICTIONARY", delimiter);
1999
if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
2000
printf ("%cFORWARD_SECRECY", delimiter);
2004
if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
2005
printf ("%cPASS_CREDENTIALS", delimiter);
2009
if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
2010
printf ("%cMUTUAL_AUTH", delimiter);
2016
printf ("\n\tfeatures:");
2019
if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
2020
printf ("%cWANT_CLIENT_FIRST", delimiter);
2024
if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
2025
printf ("%cSERVER_FIRST", delimiter);
2029
if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
2030
printf ("%cPROXY_AUTHENTICATION", delimiter);
2034
if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
2035
printf ("%cNEED_SERVER_FQDN", delimiter);
2039
/* Is this one used? */
2040
if (m->plug->features & SASL_FEAT_SERVICE) {
2041
printf ("%cSERVICE", delimiter);
2045
if (m->plug->features & SASL_FEAT_GETSECRET) {
2046
printf ("%cNEED_GETSECRET", delimiter);
2052
printf ("\n\twill be loaded from \"%s\"", m->f);
2058
/* Dump information about available server plugins (separate functions should be
2059
used for canon and auxprop plugins */
2060
int sasl_server_plugin_info (
2061
const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
2062
sasl_server_info_callback_t *info_cb,
2067
server_sasl_mechanism_t plug_data;
2069
char *mech_list = NULL;
2072
if (info_cb == NULL) {
2073
info_cb = _sasl_print_mechanism;
2076
if (mechlist != NULL) {
2077
info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
2079
if (c_mech_list == NULL) {
2080
m = mechlist->mech_list; /* m point to beginning of the list */
2083
memcpy (&plug_data, &m->m, sizeof(plug_data));
2085
info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2090
mech_list = strdup(c_mech_list);
2092
cur_mech = mech_list;
2094
while (cur_mech != NULL) {
2095
p = strchr (cur_mech, ' ');
2101
m = mechlist->mech_list; /* m point to beginning of the list */
2104
if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
2105
memcpy (&plug_data, &m->m, sizeof(plug_data));
2107
info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
2119
info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
2124
return (SASL_NOTINIT);