source: trunk/third/cyrus-sasl/lib/server.c @ 18842

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