123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110 |
- /* SASL server API implementation
- * Rob Siemborski
- * Tim Martin
- */
- /*
- * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The name "Carnegie Mellon University" must not be used to
- * endorse or promote products derived from this software without
- * prior written permission. For permission or any other legal
- * details, please contact
- * Carnegie Mellon University
- * Center for Technology Transfer and Enterprise Creation
- * 4615 Forbes Avenue
- * Suite 302
- * Pittsburgh, PA 15213
- * (412) 268-7393, fax: (412) 268-7395
- * innovation@andrew.cmu.edu
- *
- * 4. Redistributions of any form whatsoever must retain the following
- * acknowledgment:
- * "This product includes software developed by Computing Services
- * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
- *
- * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
- * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
- * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <config.h>
- /* checkpw stuff */
- #include <stdio.h>
- #include "sasl.h"
- #include "saslutil.h"
- #include "saslplug.h"
- #include "saslint.h"
- #include <assert.h>
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include <fcntl.h>
- #ifdef USE_DOORS
- #include <sys/mman.h>
- #error #include <door.h>
- #endif
- #include <stdlib.h>
- #ifndef WIN32
- #include <strings.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <sys/un.h>
- #else
- #include <string.h>
- #endif
- #include <limits.h>
- #include <sys/types.h>
- #include <ctype.h>
- #ifdef HAVE_PWD_H
- #include <pwd.h>
- #endif /* HAVE_PWD_H */
- #ifdef HAVE_SHADOW_H
- #include <shadow.h>
- #endif /* HAVE_SHADOW_H */
- #if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
- # include <errno.h>
- # include <sys/types.h>
- # include <sys/socket.h>
- # include <sys/un.h>
- # ifdef HAVE_UNISTD_H
- # include <unistd.h>
- # endif
- #endif
- /* we store the following secret to check plaintext passwords:
- *
- * <salt> \0 <secret>
- *
- * where <secret> = MD5(<salt>, "sasldb", <pass>)
- */
- static int _sasl_make_plain_secret(const char *salt,
- const char *passwd, size_t passlen,
- sasl_secret_t **secret)
- {
- MD5_CTX ctx;
- unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */
- *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) +
- sec_len * sizeof(char));
- if (*secret == NULL) {
- return SASL_NOMEM;
- }
- _sasl_MD5Init(&ctx);
- _sasl_MD5Update(&ctx, (const unsigned char *) salt, 16);
- _sasl_MD5Update(&ctx, (const unsigned char *) "sasldb", 6);
- _sasl_MD5Update(&ctx, (const unsigned char *) passwd, (unsigned int) passlen);
- memcpy((*secret)->data, salt, 16);
- (*secret)->data[16] = '\0';
- _sasl_MD5Final((*secret)->data + 17, &ctx);
- (*secret)->len = sec_len;
-
- return SASL_OK;
- }
- /* verify user password using auxprop plugins
- */
- static int auxprop_verify_password(sasl_conn_t *conn,
- const char *userstr,
- const char *passwd,
- const char *service __attribute__((unused)),
- const char *user_realm __attribute__((unused)))
- {
- int ret = SASL_FAIL;
- int result = SASL_OK;
- sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
- const char *password_request[] = { SASL_AUX_PASSWORD,
- "*cmusaslsecretPLAIN",
- NULL };
- struct propval auxprop_values[3];
-
- if (!conn || !userstr)
- return SASL_BADPARAM;
- /* We need to clear any previous results and re-canonify to
- * ensure correctness */
- prop_clear (sconn->sparams->propctx, 0);
-
- /* ensure its requested */
- result = prop_request(sconn->sparams->propctx, password_request);
- if(result != SASL_OK) return result;
- result = _sasl_canon_user_lookup (conn,
- userstr,
- 0,
- SASL_CU_AUTHID | SASL_CU_AUTHZID,
- &(conn->oparams));
- if(result != SASL_OK) return result;
-
- result = prop_getnames(sconn->sparams->propctx, password_request,
- auxprop_values);
- if (result < 0) {
- return result;
- }
- /* Verify that the returned <name>s are correct.
- But we defer checking for NULL values till after we verify
- that a passwd is specified. */
- if (!auxprop_values[0].name && !auxprop_values[1].name) {
- return SASL_NOUSER;
- }
-
- /* It is possible for us to get useful information out of just
- * the lookup, so we won't check that we have a password until now */
- if(!passwd) {
- ret = SASL_BADPARAM;
- goto done;
- }
- if ((!auxprop_values[0].values || !auxprop_values[0].values[0])
- && (!auxprop_values[1].values || !auxprop_values[1].values[0])) {
- return SASL_NOUSER;
- }
-
- /* At the point this has been called, the username has been canonified
- * and we've done the auxprop lookup. This should be easy. */
- if(auxprop_values[0].name
- && auxprop_values[0].values
- && auxprop_values[0].values[0]
- && !strcmp(auxprop_values[0].values[0], passwd)) {
- /* We have a plaintext version and it matched! */
- return SASL_OK;
- } else if(auxprop_values[1].name
- && auxprop_values[1].values
- && auxprop_values[1].values[0]) {
- const char *db_secret = auxprop_values[1].values[0];
- sasl_secret_t *construct;
-
- ret = _sasl_make_plain_secret(db_secret, passwd,
- strlen(passwd),
- &construct);
- if (ret != SASL_OK) {
- goto done;
- }
- if (!memcmp(db_secret, construct->data, construct->len)) {
- /* password verified! */
- ret = SASL_OK;
- } else {
- /* passwords do not match */
- ret = SASL_BADAUTH;
- }
- sasl_FREE(construct);
- } else {
- /* passwords do not match */
- ret = SASL_BADAUTH;
- }
- /* erase the plaintext password */
- sconn->sparams->utils->prop_erase(sconn->sparams->propctx,
- password_request[0]);
- done:
- /* We're not going to erase the property here because other people
- * may want it */
- return ret;
- }
- #if 0
- /* Verify user password using auxprop plugins. Allow verification against a hashed password,
- * or non-retrievable password. Don't use cmusaslsecretPLAIN attribute.
- *
- * This function is similar to auxprop_verify_password().
- */
- static int auxprop_verify_password_hashed(sasl_conn_t *conn,
- const char *userstr,
- const char *passwd,
- const char *service __attribute__((unused)),
- const char *user_realm __attribute__((unused)))
- {
- int ret = SASL_FAIL;
- int result = SASL_OK;
- sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
- const char *password_request[] = { SASL_AUX_PASSWORD,
- NULL };
- struct propval auxprop_values[2];
- unsigned extra_cu_flags = 0;
- if (!conn || !userstr)
- return SASL_BADPARAM;
- /* We need to clear any previous results and re-canonify to
- * ensure correctness */
- prop_clear(sconn->sparams->propctx, 0);
-
- /* ensure its requested */
- result = prop_request(sconn->sparams->propctx, password_request);
- if (result != SASL_OK) return result;
- /* We need to pass "password" down to the auxprop_lookup */
- /* NB: We don't support binary passwords */
- if (passwd != NULL) {
- prop_set (sconn->sparams->propctx,
- SASL_AUX_PASSWORD,
- passwd,
- -1);
- extra_cu_flags = SASL_CU_VERIFY_AGAINST_HASH;
- }
- result = _sasl_canon_user_lookup (conn,
- userstr,
- 0,
- SASL_CU_AUTHID | SASL_CU_AUTHZID | extra_cu_flags,
- &(conn->oparams));
- if (result != SASL_OK) return result;
-
- result = prop_getnames(sconn->sparams->propctx, password_request,
- auxprop_values);
- if (result < 0) {
- return result;
- }
- /* Verify that the returned <name>s are correct.
- But we defer checking for NULL values till after we verify
- that a passwd is specified. */
- if (!auxprop_values[0].name && !auxprop_values[1].name) {
- return SASL_NOUSER;
- }
-
- /* It is possible for us to get useful information out of just
- * the lookup, so we won't check that we have a password until now */
- if (!passwd) {
- ret = SASL_BADPARAM;
- goto done;
- }
- if ((!auxprop_values[0].values || !auxprop_values[0].values[0])) {
- return SASL_NOUSER;
- }
- /* At the point this has been called, the username has been canonified
- * and we've done the auxprop lookup. This should be easy. */
- /* NB: Note that if auxprop_lookup failed to verify the password,
- then the userPassword property value would be NULL */
- if (auxprop_values[0].name
- && auxprop_values[0].values
- && auxprop_values[0].values[0]
- && !strcmp(auxprop_values[0].values[0], passwd)) {
- /* We have a plaintext version and it matched! */
- return SASL_OK;
- } else {
- /* passwords do not match */
- ret = SASL_BADAUTH;
- }
- done:
- /* We're not going to erase the property here because other people
- * may want it */
- return ret;
- }
- #endif
- #ifdef DO_SASL_CHECKAPOP
- int _sasl_auxprop_verify_apop(sasl_conn_t *conn,
- const char *userstr,
- const char *challenge,
- const char *response,
- const char *user_realm __attribute__((unused)))
- {
- int ret = SASL_BADAUTH;
- char *userid = NULL;
- char *realm = NULL;
- unsigned char digest[16];
- char digeststr[33];
- const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
- struct propval auxprop_values[2];
- sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
- MD5_CTX ctx;
- int i;
- if (!conn || !userstr || !challenge || !response)
- PARAMERROR(conn)
- /* We've done the auxprop lookup already (in our caller) */
- /* sadly, APOP has no provision for storing secrets */
- ret = prop_getnames(sconn->sparams->propctx, password_request,
- auxprop_values);
- if(ret < 0) {
- sasl_seterror(conn, 0, "could not perform password lookup");
- goto done;
- }
-
- if(!auxprop_values[0].name ||
- !auxprop_values[0].values ||
- !auxprop_values[0].values[0]) {
- sasl_seterror(conn, 0, "could not find password");
- ret = SASL_NOUSER;
- goto done;
- }
-
- _sasl_MD5Init(&ctx);
- _sasl_MD5Update(&ctx, (const unsigned char *) challenge, strlen(challenge));
- _sasl_MD5Update(&ctx, (const unsigned char *) auxprop_values[0].values[0],
- strlen(auxprop_values[0].values[0]));
- _sasl_MD5Final(digest, &ctx);
- /* erase the plaintext password */
- sconn->sparams->utils->prop_erase(sconn->sparams->propctx,
- password_request[0]);
- /* convert digest from binary to ASCII hex */
- for (i = 0; i < 16; i++)
- sprintf(digeststr + (i*2), "%02x", digest[i]);
- if (!strncasecmp(digeststr, response, 32)) {
- /* password verified! */
- ret = SASL_OK;
- } else {
- /* passwords do not match */
- ret = SASL_BADAUTH;
- }
- done:
- if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG,
- "login incorrect");
- if (userid) sasl_FREE(userid);
- if (realm) sasl_FREE(realm);
- return ret;
- }
- #endif /* DO_SASL_CHECKAPOP */
- #if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
- /*
- * Wait for file descriptor to be writable. Return with error if timeout.
- */
- static int write_wait(int fd, unsigned delta)
- {
- fd_set wfds;
- fd_set efds;
- struct timeval tv;
- /*
- * Wait for file descriptor fd to be writable. Retry on
- * interruptions. Return with error upon timeout.
- */
- while (1) {
- FD_ZERO(&wfds);
- FD_ZERO(&efds);
- FD_SET(fd, &wfds);
- FD_SET(fd, &efds);
- tv.tv_sec = (long) delta;
- tv.tv_usec = 0;
- switch(select(fd + 1, 0, &wfds, &efds, &tv)) {
- case 0:
- /* Timeout. */
- errno = ETIMEDOUT;
- return -1;
- case +1:
- if (FD_ISSET(fd, &wfds)) {
- /* Success, file descriptor is writable. */
- return 0;
- }
- return -1;
- case -1:
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- default:
- /* Error catch-all. */
- return -1;
- }
- }
- /* Not reached. */
- return -1;
- }
- /*
- * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
- * until all the data is written out or an error/timeout occurs.
- */
- static int retry_writev(int fd, struct iovec *iov, int iovcnt, unsigned delta)
- {
- int n;
- int i;
- int written = 0;
- static int iov_max =
- #ifdef MAXIOV
- MAXIOV
- #else
- #ifdef IOV_MAX
- IOV_MAX
- #else
- 8192
- #endif
- #endif
- ;
-
- for (;;) {
- while (iovcnt && iov[0].iov_len == 0) {
- iov++;
- iovcnt--;
- }
- if (!iovcnt) return written;
- if (delta > 0) {
- if (write_wait(fd, delta))
- return -1;
- }
- n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
- if (n == -1) {
- if (errno == EINVAL && iov_max > 10) {
- iov_max /= 2;
- continue;
- }
- if (errno == EINTR) continue;
- return -1;
- }
- written += n;
- for (i = 0; i < iovcnt; i++) {
- if ((int) iov[i].iov_len > n) {
- iov[i].iov_base = (char *)iov[i].iov_base + n;
- iov[i].iov_len -= n;
- break;
- }
- n -= iov[i].iov_len;
- iov[i].iov_len = 0;
- }
- if (i == iovcnt) return written;
- }
- }
- #endif
- #ifdef HAVE_PWCHECK
- /* pwcheck daemon-authenticated login */
- static int pwcheck_verify_password(sasl_conn_t *conn,
- const char *userid,
- const char *passwd,
- const char *service __attribute__((unused)),
- const char *user_realm
- __attribute__((unused)))
- {
- int s;
- struct sockaddr_un srvaddr;
- int r;
- struct iovec iov[10];
- static char response[1024];
- unsigned start, n;
- char pwpath[1024];
- if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL;
- strcpy(pwpath, PWCHECKDIR);
- strcat(pwpath, "/pwcheck");
- s = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s == -1) return errno;
- memset((char *)&srvaddr, 0, sizeof(srvaddr));
- srvaddr.sun_family = AF_UNIX;
- strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
- r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
- if (r == -1) {
- sasl_seterror(conn,0,"cannot connect to pwcheck server");
- return SASL_FAIL;
- }
- iov[0].iov_base = (char *)userid;
- iov[0].iov_len = strlen(userid)+1;
- iov[1].iov_base = (char *)passwd;
- iov[1].iov_len = strlen(passwd)+1;
- retry_writev(s, iov, 2, 0);
- start = 0;
- while (start < sizeof(response) - 1) {
- n = read(s, response+start, sizeof(response) - 1 - start);
- if (n < 1) break;
- start += n;
- }
- close(s);
- if (start > 1 && !strncmp(response, "OK", 2)) {
- return SASL_OK;
- }
- response[start] = '\0';
- sasl_seterror(conn,0,response);
- return SASL_BADAUTH;
- }
- #endif
- #if defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
- static int read_wait(int fd, unsigned delta)
- {
- fd_set rfds;
- fd_set efds;
- struct timeval tv;
- /*
- * Wait for file descriptor fd to be readable. Retry on
- * interruptions. Return with error upon timeout.
- */
- while (1) {
- FD_ZERO(&rfds);
- FD_ZERO(&efds);
- FD_SET(fd, &rfds);
- FD_SET(fd, &efds);
- tv.tv_sec = (long) delta;
- tv.tv_usec = 0;
- switch(select(fd + 1, &rfds, 0, &efds, &tv)) {
- case 0:
- /* Timeout. */
- errno = ETIMEDOUT;
- return -1;
- case +1:
- case +2:
- if (FD_ISSET(fd, &rfds)) {
- /* Success, file descriptor is readable. */
- return 0;
- }
- return -1;
- case -1:
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- default:
- /* Error catch-all. */
- return -1;
- }
- }
- /* Not reached. */
- return -1;
- }
- /*
- * Keep calling the read() system call until all the data is read in,
- * timeout, EOF, or an error occurs. This function returns the number
- * of useful bytes, or -1 if timeout/error.
- */
- static int retry_read(int fd, void *buf0, unsigned nbyte, unsigned delta)
- {
- int nr;
- unsigned nleft = nbyte;
- char *buf = (char*) buf0;
-
- while (nleft >= 1) {
- if (delta > 0) {
- if (read_wait(fd, delta))
- return -1;
- }
- nr = read(fd, buf, nleft);
- if (nr < 0) {
- if (errno == EINTR || errno == EAGAIN)
- continue;
- return -1;
- } else if (nr == 0) {
- break;
- }
- buf += nr;
- nleft -= nr;
- }
- return nbyte - nleft;
- }
- #endif
- #ifdef HAVE_SASLAUTHD
- /* saslauthd-authenticated login */
- static int saslauthd_verify_password(sasl_conn_t *conn,
- const char *userid,
- const char *passwd,
- const char *service,
- const char *user_realm)
- {
- char response[1024];
- char query[8192];
- char *query_end = query;
- int s;
- struct sockaddr_un srvaddr;
- sasl_getopt_t *getopt;
- void *context;
- char pwpath[sizeof(srvaddr.sun_path)];
- const char *p = NULL;
- char *freeme = NULL;
- #ifdef USE_DOORS
- door_arg_t arg;
- #endif
- /* check to see if the user configured a rundir */
- if (_sasl_getcallback(conn, SASL_CB_GETOPT,
- (sasl_callback_ft *)&getopt, &context) == SASL_OK) {
- getopt(context, NULL, "saslauthd_path", &p, NULL);
- }
- if (p) {
- if (strlen(p) >= sizeof(pwpath))
- return SASL_FAIL;
- strncpy(pwpath, p, sizeof(pwpath) - 1);
- pwpath[strlen(p)] = '\0';
- } else {
- if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath))
- return SASL_FAIL;
- strcpy(pwpath, PATH_SASLAUTHD_RUNDIR "/mux");
- }
- /* Split out username/realm if necessary */
- if(strrchr(userid,'@') != NULL) {
- char *rtmp;
-
- if(_sasl_strdup(userid, &freeme, NULL) != SASL_OK)
- goto fail;
- userid = freeme;
- rtmp = strrchr(userid,'@');
- *rtmp = '\0';
- user_realm = rtmp + 1;
- }
- /*
- * build request of the form:
- *
- * count authid count password count service count realm
- */
- {
- unsigned short max_len, req_len, u_len, p_len, s_len, r_len;
-
- max_len = (unsigned short) sizeof(query);
- /* prevent buffer overflow */
- if ((strlen(userid) > USHRT_MAX) ||
- (strlen(passwd) > USHRT_MAX) ||
- (strlen(service) > USHRT_MAX) ||
- (user_realm && (strlen(user_realm) > USHRT_MAX))) {
- goto toobig;
- }
- u_len = (strlen(userid));
- p_len = (strlen(passwd));
- s_len = (strlen(service));
- r_len = ((user_realm ? strlen(user_realm) : 0));
- /* prevent buffer overflow */
- req_len = 30;
- if (max_len - req_len < u_len) goto toobig;
- req_len += u_len;
- if (max_len - req_len < p_len) goto toobig;
- req_len += p_len;
- if (max_len - req_len < s_len) goto toobig;
- req_len += s_len;
- if (max_len - req_len < r_len) goto toobig;
- u_len = htons(u_len);
- p_len = htons(p_len);
- s_len = htons(s_len);
- r_len = htons(r_len);
- memcpy(query_end, &u_len, sizeof(unsigned short));
- query_end += sizeof(unsigned short);
- while (*userid) *query_end++ = *userid++;
- memcpy(query_end, &p_len, sizeof(unsigned short));
- query_end += sizeof(unsigned short);
- while (*passwd) *query_end++ = *passwd++;
- memcpy(query_end, &s_len, sizeof(unsigned short));
- query_end += sizeof(unsigned short);
- while (*service) *query_end++ = *service++;
- memcpy(query_end, &r_len, sizeof(unsigned short));
- query_end += sizeof(unsigned short);
- if (user_realm) while (*user_realm) *query_end++ = *user_realm++;
- }
- #ifdef USE_DOORS
- s = open(pwpath, O_RDONLY);
- if (s < 0) {
- sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno);
- goto fail;
- }
- arg.data_ptr = query;
- arg.data_size = query_end - query;
- arg.desc_ptr = NULL;
- arg.desc_num = 0;
- arg.rbuf = response;
- arg.rsize = sizeof(response);
- if (door_call(s, &arg) < 0) {
- /* Parameters are undefined */
- close(s);
- sasl_seterror(conn, 0, "door call to saslauthd server failed: %m", errno);
- goto fail;
- }
- if (arg.data_ptr != response || arg.data_size >= sizeof(response)) {
- /* oh damn, we got back a really long response */
- munmap(arg.rbuf, arg.rsize);
- close(s);
- sasl_seterror(conn, 0, "saslauthd sent an overly long response");
- goto fail;
- }
- response[arg.data_size] = '\0';
- close(s);
- #else
- /* unix sockets */
- s = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s == -1) {
- sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno);
- goto fail;
- }
- memset((char *)&srvaddr, 0, sizeof(srvaddr));
- srvaddr.sun_family = AF_UNIX;
- strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path) - 1);
- srvaddr.sun_path[strlen(pwpath)] = '\0';
- {
- int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
- if (r == -1) {
- close(s);
- sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno);
- goto fail;
- }
- }
- {
- struct iovec iov[8];
-
- iov[0].iov_len = query_end - query;
- iov[0].iov_base = query;
- if (retry_writev(s, iov, 1, 0) == -1) {
- close(s);
- sasl_seterror(conn, 0, "write failed");
- goto fail;
- }
- }
- {
- unsigned short count = 0;
- /*
- * read response of the form:
- *
- * count result
- */
- if (retry_read(s, &count, sizeof(count), 0) < (int) sizeof(count)) {
- sasl_seterror(conn, 0, "size read failed");
- goto fail;
- }
-
- count = ntohs(count);
- if (count < 2) { /* MUST have at least "OK" or "NO" */
- close(s);
- sasl_seterror(conn, 0, "bad response from saslauthd");
- goto fail;
- }
-
- count = (int)sizeof(response) <= count ? sizeof(response) - 1 : count;
- if (retry_read(s, response, count, 0) < count) {
- close(s);
- sasl_seterror(conn, 0, "read failed");
- goto fail;
- }
- response[count] = '\0';
- }
- close(s);
- #endif /* USE_DOORS */
-
- if(freeme) free(freeme);
- if (!strncmp(response, "OK", 2)) {
- return SASL_OK;
- }
-
- sasl_seterror(conn, SASL_NOLOG, "authentication failed");
- return SASL_BADAUTH;
- toobig:
- /* request just too damn big */
- sasl_seterror(conn, 0, "saslauthd request too large");
- fail:
- if (freeme) free(freeme);
- return SASL_FAIL;
- }
- #endif
- #ifdef HAVE_AUTHDAEMON
- /*
- * Preliminary support for Courier's authdaemond.
- */
- #define AUTHDAEMON_IO_TIMEOUT 30
- static int authdaemon_blocking(int fd, int block)
- {
- int f, r;
- /* Get the fd's blocking bit. */
- f = fcntl(fd, F_GETFL, 0);
- if (f == -1)
- return -1;
- /* Adjust the bitmap accordingly. */
- #ifndef O_NONBLOCK
- #define NB_BITMASK FNDELAY
- #else
- #define NB_BITMASK O_NONBLOCK
- #endif
- if (block)
- f &= ~NB_BITMASK;
- else
- f |= NB_BITMASK;
- #undef NB_BITMASK
- /* Adjust the fd's blocking bit. */
- r = fcntl(fd, F_SETFL, f);
- if (r)
- return -1;
- /* Success. */
- return 0;
- }
- static int authdaemon_connect(sasl_conn_t *conn, const char *path)
- {
- int r, s = -1;
- struct sockaddr_un srvaddr;
- if (strlen(path) >= sizeof(srvaddr.sun_path)) {
- sasl_seterror(conn, 0, "unix socket path too large", errno);
- goto fail;
- }
- s = socket(AF_UNIX, SOCK_STREAM, 0);
- if (s == -1) {
- sasl_seterror(conn, 0, "cannot create socket for connection to Courier authdaemond: %m", errno);
- goto fail;
- }
- memset((char *)&srvaddr, 0, sizeof(srvaddr));
- srvaddr.sun_family = AF_UNIX;
- strncpy(srvaddr.sun_path, path, sizeof(srvaddr.sun_path) - 1);
- /* Use nonblocking unix socket connect(2). */
- if (authdaemon_blocking(s, 0)) {
- sasl_seterror(conn, 0, "cannot set nonblocking bit: %m", errno);
- goto fail;
- }
- r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
- if (r == -1) {
- sasl_seterror(conn, 0, "cannot connect to Courier authdaemond: %m", errno);
- goto fail;
- }
- if (authdaemon_blocking(s, 1)) {
- sasl_seterror(conn, 0, "cannot clear nonblocking bit: %m", errno);
- goto fail;
- }
- return s;
- fail:
- if (s >= 0)
- close(s);
- return -1;
- }
- static char *authdaemon_build_query(const char *service,
- const char *authtype,
- const char *user,
- const char *passwd)
- {
- int sz;
- int l = strlen(service)
- + 1
- + strlen(authtype)
- + 1
- + strlen(user)
- + 1
- + strlen(passwd)
- + 1;
- char *buf, n[5];
- if (snprintf(n, sizeof(n), "%d", l) >= (int)sizeof(n))
- return NULL;
- sz = strlen(n) + l + 20;
- if (!(buf = sasl_ALLOC(sz)))
- return NULL;
- snprintf(buf,
- sz,
- "AUTH %s\n%s\n%s\n%s\n%s\n\n",
- n,
- service,
- authtype,
- user,
- passwd);
- return buf;
- }
- static int authdaemon_read(int fd, void *buf0, unsigned sz)
- {
- int nr;
- char *buf = (char*) buf0;
- if (sz <= 1)
- return -1;
- if ((nr = retry_read(fd, buf0, sz - 1, AUTHDAEMON_IO_TIMEOUT)) < 0)
- return -1;
- /* We need a null-terminated buffer. */
- buf[nr] = 0;
- /* Check for overflow condition. */
- return nr + 1 < (int)sz ? 0 : -1;
- }
- static int authdaemon_write(int fd, void *buf0, unsigned sz)
- {
- int nw;
- struct iovec io;
- io.iov_len = sz;
- io.iov_base = buf0;
- nw = retry_writev(fd, &io, 1, AUTHDAEMON_IO_TIMEOUT);
- return nw == (int)sz ? 0 : -1;
- }
- static int authdaemon_talk(sasl_conn_t *conn, int sock, char *authreq)
- {
- char *str;
- char buf[8192];
- if (authdaemon_write(sock, authreq, strlen(authreq)))
- goto _err_out;
- if (authdaemon_read(sock, buf, sizeof(buf)))
- goto _err_out;
- for (str = buf; *str; ) {
- char *sub;
- for (sub = str; *str; ++str) {
- if (*str == '\n') {
- *str++ = 0;
- break;
- }
- }
- if (strcmp(sub, ".") == 0) {
- /* success */
- return SASL_OK;
- }
- if (strcmp(sub, "FAIL") == 0) {
- /* passwords do not match */
- sasl_seterror(conn, SASL_NOLOG, "authentication failed");
- return SASL_BADAUTH;
- }
- }
- _err_out:
- /* catchall: authentication error */
- sasl_seterror(conn, 0, "could not verify password");
- return SASL_FAIL;
- }
- static int authdaemon_verify_password(sasl_conn_t *conn,
- const char *userid,
- const char *passwd,
- const char *service,
- const char *user_realm __attribute__((unused)))
- {
- const char *p = NULL;
- sasl_getopt_t *getopt;
- void *context;
- int result = SASL_FAIL;
- char *query = NULL;
- int sock = -1;
- /* check to see if the user configured a rundir */
- if (_sasl_getcallback(conn, SASL_CB_GETOPT,
- (sasl_callback_ft *)&getopt, &context) == SASL_OK) {
- getopt(context, NULL, "authdaemond_path", &p, NULL);
- }
- if (!p) {
- /*
- * XXX should we peek at Courier's build-time config ?
- */
- p = PATH_AUTHDAEMON_SOCKET;
- }
- if ((sock = authdaemon_connect(conn, p)) < 0)
- goto out;
- if (!(query = authdaemon_build_query(service, "login", userid, passwd)))
- goto out;
- result = authdaemon_talk(conn, sock, query);
- out:
- if (sock >= 0)
- close(sock), sock = -1;
- if (query)
- sasl_FREE(query), query = 0;
- return result;
- }
- #endif
- #ifdef HAVE_ALWAYSTRUE
- static int always_true(sasl_conn_t *conn,
- const char *userstr,
- const char *passwd __attribute__((unused)),
- const char *service __attribute__((unused)),
- const char *user_realm __attribute__((unused)))
- {
- _sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s",
- userstr);
- return SASL_OK;
- }
- #endif
- struct sasl_verify_password_s _sasl_verify_password[] = {
- { "auxprop", &auxprop_verify_password },
- #if 0 /* totally undocumented. wtf is this? */
- { "auxprop-hashed", &auxprop_verify_password_hashed },
- #endif
- #ifdef HAVE_PWCHECK
- { "pwcheck", &pwcheck_verify_password },
- #endif
- #ifdef HAVE_SASLAUTHD
- { "saslauthd", &saslauthd_verify_password },
- #endif
- #ifdef HAVE_AUTHDAEMON
- { "authdaemond", &authdaemon_verify_password },
- #endif
- #ifdef HAVE_ALWAYSTRUE
- { "alwaystrue", &always_true },
- #endif
- { NULL, NULL }
- };
|