1
/* SASL server API implementation
4
* $Id: checkpw.c,v 1.73 2006/03/13 18:30:41 mel 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.
71
#include <netinet/in.h>
77
#include <sys/types.h>
82
#endif /* HAVE_PWD_H */
85
#endif /* HAVE_SHADOW_H */
87
#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
89
# include <sys/types.h>
90
# include <sys/socket.h>
98
/* we store the following secret to check plaintext passwords:
102
* where <secret> = MD5(<salt>, "sasldb", <pass>)
104
static int _sasl_make_plain_secret(const char *salt,
105
const char *passwd, size_t passlen,
106
sasl_secret_t **secret)
109
unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */
111
*secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) +
112
sec_len * sizeof(char));
113
if (*secret == NULL) {
118
_sasl_MD5Update(&ctx, salt, 16);
119
_sasl_MD5Update(&ctx, "sasldb", 6);
120
_sasl_MD5Update(&ctx, passwd, (unsigned int) passlen);
121
memcpy((*secret)->data, salt, 16);
122
(*secret)->data[16] = '\0';
123
_sasl_MD5Final((*secret)->data + 17, &ctx);
124
(*secret)->len = sec_len;
129
/* erase & dispose of a sasl_secret_t
131
static int auxprop_verify_password(sasl_conn_t *conn,
134
const char *service __attribute__((unused)),
135
const char *user_realm __attribute__((unused)))
140
int result = SASL_OK;
141
sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
142
const char *password_request[] = { SASL_AUX_PASSWORD,
143
"*cmusaslsecretPLAIN",
145
struct propval auxprop_values[3];
147
if (!conn || !userstr)
148
return SASL_BADPARAM;
150
/* We need to clear any previous results and re-canonify to
151
* ensure correctness */
153
prop_clear(sconn->sparams->propctx, 0);
155
/* ensure its requested */
156
result = prop_request(sconn->sparams->propctx, password_request);
158
if(result != SASL_OK) return result;
160
result = _sasl_canon_user(conn, userstr, 0,
161
SASL_CU_AUTHID | SASL_CU_AUTHZID,
163
if(result != SASL_OK) return result;
165
result = prop_getnames(sconn->sparams->propctx, password_request,
170
if((!auxprop_values[0].name
171
|| !auxprop_values[0].values || !auxprop_values[0].values[0])
172
&& (!auxprop_values[1].name
173
|| !auxprop_values[1].values || !auxprop_values[1].values[0]))
176
/* It is possible for us to get useful information out of just
177
* the lookup, so we won't check that we have a password until now */
183
/* At the point this has been called, the username has been canonified
184
* and we've done the auxprop lookup. This should be easy. */
185
if(auxprop_values[0].name
186
&& auxprop_values[0].values
187
&& auxprop_values[0].values[0]
188
&& !strcmp(auxprop_values[0].values[0], passwd)) {
189
/* We have a plaintext version and it matched! */
191
} else if(auxprop_values[1].name
192
&& auxprop_values[1].values
193
&& auxprop_values[1].values[0]) {
194
const char *db_secret = auxprop_values[1].values[0];
195
sasl_secret_t *construct;
197
ret = _sasl_make_plain_secret(db_secret, passwd,
200
if (ret != SASL_OK) {
204
if (!memcmp(db_secret, construct->data, construct->len)) {
205
/* password verified! */
208
/* passwords do not match */
212
sasl_FREE(construct);
214
/* passwords do not match */
218
/* erase the plaintext password */
219
sconn->sparams->utils->prop_erase(sconn->sparams->propctx,
220
password_request[0]);
223
if (userid) sasl_FREE(userid);
224
if (realm) sasl_FREE(realm);
226
/* We're not going to erase the property here because other people
231
#ifdef DO_SASL_CHECKAPOP
232
int _sasl_auxprop_verify_apop(sasl_conn_t *conn,
234
const char *challenge,
235
const char *response,
236
const char *user_realm __attribute__((unused)))
238
int ret = SASL_BADAUTH;
241
unsigned char digest[16];
243
const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
244
struct propval auxprop_values[2];
245
sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
249
if (!conn || !userstr || !challenge || !response)
252
/* We've done the auxprop lookup already (in our caller) */
253
/* sadly, APOP has no provision for storing secrets */
254
ret = prop_getnames(sconn->sparams->propctx, password_request,
257
sasl_seterror(conn, 0, "could not perform password lookup");
261
if(!auxprop_values[0].name ||
262
!auxprop_values[0].values ||
263
!auxprop_values[0].values[0]) {
264
sasl_seterror(conn, 0, "could not find password");
270
_sasl_MD5Update(&ctx, challenge, strlen(challenge));
271
_sasl_MD5Update(&ctx, auxprop_values[0].values[0],
272
strlen(auxprop_values[0].values[0]));
273
_sasl_MD5Final(digest, &ctx);
275
/* erase the plaintext password */
276
sconn->sparams->utils->prop_erase(sconn->sparams->propctx,
277
password_request[0]);
279
/* convert digest from binary to ASCII hex */
280
for (i = 0; i < 16; i++)
281
sprintf(digeststr + (i*2), "%02x", digest[i]);
283
if (!strncasecmp(digeststr, response, 32)) {
284
/* password verified! */
287
/* passwords do not match */
292
if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG,
294
if (userid) sasl_FREE(userid);
295
if (realm) sasl_FREE(realm);
299
#endif /* DO_SASL_CHECKAPOP */
301
#if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
303
* Wait for file descriptor to be writable. Return with error if timeout.
305
static int write_wait(int fd, unsigned delta)
312
* Wait for file descriptor fd to be writable. Retry on
313
* interruptions. Return with error upon timeout.
320
tv.tv_sec = (long) delta;
322
switch(select(fd + 1, 0, &wfds, &efds, &tv)) {
328
if (FD_ISSET(fd, &wfds)) {
329
/* Success, file descriptor is writable. */
334
if (errno == EINTR || errno == EAGAIN)
337
/* Error catch-all. */
346
* Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
347
* until all the data is written out or an error/timeout occurs.
349
static int retry_writev(int fd, struct iovec *iov, int iovcnt, unsigned delta)
367
while (iovcnt && iov[0].iov_len == 0) {
372
if (!iovcnt) return written;
375
if (write_wait(fd, delta))
378
n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
380
if (errno == EINVAL && iov_max > 10) {
384
if (errno == EINTR) continue;
390
for (i = 0; i < iovcnt; i++) {
391
if ((int) iov[i].iov_len > n) {
392
iov[i].iov_base = (char *)iov[i].iov_base + n;
400
if (i == iovcnt) return written;
407
/* pwcheck daemon-authenticated login */
408
static int pwcheck_verify_password(sasl_conn_t *conn,
411
const char *service __attribute__((unused)),
412
const char *user_realm
413
__attribute__((unused)))
416
struct sockaddr_un srvaddr;
418
struct iovec iov[10];
419
static char response[1024];
423
if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL;
425
strcpy(pwpath, PWCHECKDIR);
426
strcat(pwpath, "/pwcheck");
428
s = socket(AF_UNIX, SOCK_STREAM, 0);
429
if (s == -1) return errno;
431
memset((char *)&srvaddr, 0, sizeof(srvaddr));
432
srvaddr.sun_family = AF_UNIX;
433
strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
434
r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
436
sasl_seterror(conn,0,"cannot connect to pwcheck server");
440
iov[0].iov_base = (char *)userid;
441
iov[0].iov_len = strlen(userid)+1;
442
iov[1].iov_base = (char *)passwd;
443
iov[1].iov_len = strlen(passwd)+1;
445
retry_writev(s, iov, 2, 0);
448
while (start < sizeof(response) - 1) {
449
n = read(s, response+start, sizeof(response) - 1 - start);
456
if (start > 1 && !strncmp(response, "OK", 2)) {
460
response[start] = '\0';
461
sasl_seterror(conn,0,response);
467
#if defined(HAVE_SASLAUTHD) || defined(HAVE_AUTHDAEMON)
468
static int read_wait(int fd, unsigned delta)
474
* Wait for file descriptor fd to be readable. Retry on
475
* interruptions. Return with error upon timeout.
482
tv.tv_sec = (long) delta;
484
switch(select(fd + 1, &rfds, 0, &efds, &tv)) {
490
if (FD_ISSET(fd, &rfds)) {
491
/* Success, file descriptor is readable. */
496
if (errno == EINTR || errno == EAGAIN)
499
/* Error catch-all. */
508
* Keep calling the read() system call until all the data is read in,
509
* timeout, EOF, or an error occurs. This function returns the number
510
* of useful bytes, or -1 if timeout/error.
512
static int retry_read(int fd, void *buf0, unsigned nbyte, unsigned delta)
515
unsigned nleft = nbyte;
516
char *buf = (char*) buf0;
520
if (read_wait(fd, delta))
523
nr = read(fd, buf, nleft);
525
if (errno == EINTR || errno == EAGAIN)
528
} else if (nr == 0) {
534
return nbyte - nleft;
538
#ifdef HAVE_SASLAUTHD
539
/* saslauthd-authenticated login */
540
static int saslauthd_verify_password(sasl_conn_t *conn,
544
const char *user_realm)
548
char *query_end = query;
550
struct sockaddr_un srvaddr;
551
sasl_getopt_t *getopt;
553
char pwpath[sizeof(srvaddr.sun_path)];
554
const char *p = NULL;
560
/* check to see if the user configured a rundir */
561
if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
562
getopt(context, NULL, "saslauthd_path", &p, NULL);
565
strncpy(pwpath, p, sizeof(pwpath));
567
if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath))
570
strcpy(pwpath, PATH_SASLAUTHD_RUNDIR);
571
strcat(pwpath, "/mux");
574
/* Split out username/realm if necessary */
575
if(strrchr(userid,'@') != NULL) {
578
if(_sasl_strdup(userid, &freeme, NULL) != SASL_OK)
582
rtmp = strrchr(userid,'@');
584
user_realm = rtmp + 1;
588
* build request of the form:
590
* count authid count password count service count realm
593
unsigned short u_len, p_len, s_len, r_len;
595
u_len = (strlen(userid));
596
p_len = (strlen(passwd));
597
s_len = (strlen(service));
598
r_len = ((user_realm ? strlen(user_realm) : 0));
600
if (u_len + p_len + s_len + r_len + 30 > (unsigned short) sizeof(query)) {
601
/* request just too damn big */
602
sasl_seterror(conn, 0, "saslauthd request too large");
606
u_len = htons(u_len);
607
p_len = htons(p_len);
608
s_len = htons(s_len);
609
r_len = htons(r_len);
611
memcpy(query_end, &u_len, sizeof(unsigned short));
612
query_end += sizeof(unsigned short);
613
while (*userid) *query_end++ = *userid++;
615
memcpy(query_end, &p_len, sizeof(unsigned short));
616
query_end += sizeof(unsigned short);
617
while (*passwd) *query_end++ = *passwd++;
619
memcpy(query_end, &s_len, sizeof(unsigned short));
620
query_end += sizeof(unsigned short);
621
while (*service) *query_end++ = *service++;
623
memcpy(query_end, &r_len, sizeof(unsigned short));
624
query_end += sizeof(unsigned short);
625
if (user_realm) while (*user_realm) *query_end++ = *user_realm++;
629
s = open(pwpath, O_RDONLY);
631
sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno);
635
arg.data_ptr = query;
636
arg.data_size = query_end - query;
640
arg.rsize = sizeof(response);
642
if (door_call(s, &arg) < 0) {
643
/* Parameters are undefined */
645
sasl_seterror(conn, 0, "door call to saslauthd server failed: %m", errno);
649
if (arg.data_ptr != response || arg.data_size >= sizeof(response)) {
650
/* oh damn, we got back a really long response */
651
munmap(arg.rbuf, arg.rsize);
653
sasl_seterror(conn, 0, "saslauthd sent an overly long response");
656
response[arg.data_size] = '\0';
662
s = socket(AF_UNIX, SOCK_STREAM, 0);
664
sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno);
668
memset((char *)&srvaddr, 0, sizeof(srvaddr));
669
srvaddr.sun_family = AF_UNIX;
670
strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
673
int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
676
sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno);
684
iov[0].iov_len = query_end - query;
685
iov[0].iov_base = query;
687
if (retry_writev(s, iov, 1, 0) == -1) {
689
sasl_seterror(conn, 0, "write failed");
695
unsigned short count = 0;
698
* read response of the form:
702
if (retry_read(s, &count, sizeof(count), 0) < (int) sizeof(count)) {
703
sasl_seterror(conn, 0, "size read failed");
707
count = ntohs(count);
708
if (count < 2) { /* MUST have at least "OK" or "NO" */
710
sasl_seterror(conn, 0, "bad response from saslauthd");
714
count = (int)sizeof(response) <= count ? sizeof(response) - 1 : count;
715
if (retry_read(s, response, count, 0) < count) {
717
sasl_seterror(conn, 0, "read failed");
720
response[count] = '\0';
724
#endif /* USE_DOORS */
726
if(freeme) free(freeme);
728
if (!strncmp(response, "OK", 2)) {
732
sasl_seterror(conn, SASL_NOLOG, "authentication failed");
736
if (freeme) free(freeme);
742
#ifdef HAVE_AUTHDAEMON
744
* Preliminary support for Courier's authdaemond.
746
#define AUTHDAEMON_IO_TIMEOUT 30
748
static int authdaemon_blocking(int fd, int block)
752
/* Get the fd's blocking bit. */
753
f = fcntl(fd, F_GETFL, 0);
757
/* Adjust the bitmap accordingly. */
759
#define NB_BITMASK FNDELAY
761
#define NB_BITMASK O_NONBLOCK
769
/* Adjust the fd's blocking bit. */
770
r = fcntl(fd, F_SETFL, f);
778
static int authdaemon_connect(sasl_conn_t *conn, const char *path)
781
struct sockaddr_un srvaddr;
783
if (strlen(path) >= sizeof(srvaddr.sun_path)) {
784
sasl_seterror(conn, 0, "unix socket path too large", errno);
788
s = socket(AF_UNIX, SOCK_STREAM, 0);
790
sasl_seterror(conn, 0, "cannot create socket for connection to Courier authdaemond: %m", errno);
794
memset((char *)&srvaddr, 0, sizeof(srvaddr));
795
srvaddr.sun_family = AF_UNIX;
796
strncpy(srvaddr.sun_path, path, sizeof(srvaddr.sun_path) - 1);
798
/* Use nonblocking unix socket connect(2). */
799
if (authdaemon_blocking(s, 0)) {
800
sasl_seterror(conn, 0, "cannot set nonblocking bit: %m", errno);
804
r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
806
sasl_seterror(conn, 0, "cannot connect to Courier authdaemond: %m", errno);
810
if (authdaemon_blocking(s, 1)) {
811
sasl_seterror(conn, 0, "cannot clear nonblocking bit: %m", errno);
822
static char *authdaemon_build_query(const char *service,
823
const char *authtype,
828
int l = strlen(service)
837
if (snprintf(n, sizeof(n), "%d", l) >= (int)sizeof(n))
839
sz = strlen(n) + l + 20;
840
if (!(buf = sasl_ALLOC(sz)))
844
"AUTH %s\n%s\n%s\n%s\n%s\n\n",
853
static int authdaemon_read(int fd, void *buf0, unsigned sz)
856
char *buf = (char*) buf0;
859
if ((nr = retry_read(fd, buf0, sz - 1, AUTHDAEMON_IO_TIMEOUT)) < 0)
861
/* We need a null-terminated buffer. */
863
/* Check for overflow condition. */
864
return nr + 1 < (int)sz ? 0 : -1;
867
static int authdaemon_write(int fd, void *buf0, unsigned sz)
873
nw = retry_writev(fd, &io, 1, AUTHDAEMON_IO_TIMEOUT);
874
return nw == (int)sz ? 0 : -1;
877
static int authdaemon_talk(sasl_conn_t *conn, int sock, char *authreq)
882
if (authdaemon_write(sock, authreq, strlen(authreq)))
884
if (authdaemon_read(sock, buf, sizeof(buf)))
886
for (str = buf; *str; ) {
889
for (sub = str; *str; ++str) {
895
if (strcmp(sub, ".") == 0) {
899
if (strcmp(sub, "FAIL") == 0) {
900
/* passwords do not match */
901
sasl_seterror(conn, SASL_NOLOG, "authentication failed");
906
/* catchall: authentication error */
907
sasl_seterror(conn, 0, "could not verify password");
911
static int authdaemon_verify_password(sasl_conn_t *conn,
915
const char *user_realm __attribute__((unused)))
917
const char *p = NULL;
918
sasl_getopt_t *getopt;
920
int result = SASL_FAIL;
924
/* check to see if the user configured a rundir */
925
if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
926
getopt(context, NULL, "authdaemond_path", &p, NULL);
930
* XXX should we peek at Courier's build-time config ?
932
p = PATH_AUTHDAEMON_SOCKET;
935
if ((sock = authdaemon_connect(conn, p)) < 0)
937
if (!(query = authdaemon_build_query(service, "login", userid, passwd)))
939
result = authdaemon_talk(conn, sock, query);
942
close(sock), sock = -1;
944
sasl_FREE(query), query = 0;
949
#ifdef HAVE_ALWAYSTRUE
950
static int always_true(sasl_conn_t *conn,
952
const char *passwd __attribute__((unused)),
953
const char *service __attribute__((unused)),
954
const char *user_realm __attribute__((unused)))
956
_sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s",
962
struct sasl_verify_password_s _sasl_verify_password[] = {
963
{ "auxprop", &auxprop_verify_password },
965
{ "pwcheck", &pwcheck_verify_password },
967
#ifdef HAVE_SASLAUTHD
968
{ "saslauthd", &saslauthd_verify_password },
970
#ifdef HAVE_AUTHDAEMON
971
{ "authdaemond", &authdaemon_verify_password },
973
#ifdef HAVE_ALWAYSTRUE
974
{ "alwaystrue", &always_true },