1 /* SASL server API implementation
4 * $Id: checkpw.c,v 1.49 2002/03/07 19:14:04 ken3 Exp $
7 * Copyright (c) 2001 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.
47 * Taken from Cyrus-SASL library and adapted by Alexander S. Sabourenkov
48 * Oct 2001 - Apr 2002: Slightly modified by Philip Hazel.
49 * Aug 2003: new code for saslauthd from Alexander S. Sabourenkov incorporated
50 * by Philip Hazel (minor mods to avoid compiler warnings)
51 * Oct 2006: (PH) removed redundant tests on "reply" being NULL - some were
52 * missing, and confused someone who was using this code for some
53 * other purpose. Here in Exim, "reply" is never NULL.
55 * screwdriver@lxnt.info
59 /* Originally this module supported only the pwcheck daemon, which is where its
60 name comes from. Nowadays it supports saslauthd as well; pwcheck is in fact
61 deprecated. The definitions of CYRUS_PWCHECK_SOCKET and CYRUS_SASLAUTHD_SOCKET
62 determine whether the facilities are actually supported or not. */
69 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
73 static int retry_read(int, void *, unsigned );
74 static int retry_writev(int, struct iovec
*, int );
75 static int read_string(int, uschar
**);
76 static int write_string(int, const uschar
*, int);
81 /* A dummy function that always fails if pwcheck support is not
84 #ifndef CYRUS_PWCHECK_SOCKET
85 int pwcheck_verify_password(const char *userid
,
89 userid
= userid
; /* Keep picky compilers happy */
91 *reply
= "pwcheck support is not included in this Exim binary";
96 /* This is the real function */
100 /* taken from cyrus-sasl file checkpw.c */
101 /* pwcheck daemon-authenticated login */
102 int pwcheck_verify_password(const char *userid
,
107 struct sockaddr_un srvaddr
;
109 static char response
[1024];
113 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
114 if (s
== -1) { return PWCHECK_FAIL
; }
116 memset((char *)&srvaddr
, 0, sizeof(srvaddr
));
117 srvaddr
.sun_family
= AF_UNIX
;
118 strncpy(srvaddr
.sun_path
, CYRUS_PWCHECK_SOCKET
, sizeof(srvaddr
.sun_path
));
119 r
= connect(s
, (struct sockaddr
*)&srvaddr
, sizeof(srvaddr
));
122 debug_printf("Cannot connect to pwcheck daemon (at '%s')\n",CYRUS_PWCHECK_SOCKET
);
123 *reply
= "cannot connect to pwcheck daemon";
127 iov
[0].iov_base
= (char *)userid
;
128 iov
[0].iov_len
= strlen(userid
)+1;
129 iov
[1].iov_base
= (char *)passwd
;
130 iov
[1].iov_len
= strlen(passwd
)+1;
132 retry_writev(s
, iov
, 2);
135 while (start
< sizeof(response
) - 1) {
136 n
= read(s
, response
+start
, sizeof(response
) - 1 - start
);
143 if (start
> 1 && !strncmp(response
, "OK", 2)) {
147 response
[start
] = '\0';
156 /* A dummy function that always fails if saslauthd support is not
159 #ifndef CYRUS_SASLAUTHD_SOCKET
160 int saslauthd_verify_password(const uschar
*userid
,
161 const uschar
*passwd
,
162 const uschar
*service
,
164 const uschar
**reply
)
166 userid
= userid
; /* Keep picky compilers happy */
170 *reply
= US
"saslauthd support is not included in this Exim binary";
175 /* This is the real function */
178 /* written from scratch */
179 /* saslauthd daemon-authenticated login */
181 int saslauthd_verify_password(const uschar
*userid
,
182 const uschar
*password
,
183 const uschar
*service
,
185 const uschar
**reply
)
187 uschar
*daemon_reply
= NULL
;
189 struct sockaddr_un srvaddr
;
192 debug_printf("saslauthd userid='%s' servicename='%s'"
193 " realm='%s'\n", userid
, service
, realm
);
197 s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
199 *reply
= CUstrerror(errno
);
203 memset((char *)&srvaddr
, 0, sizeof(srvaddr
));
204 srvaddr
.sun_family
= AF_UNIX
;
205 strncpy(srvaddr
.sun_path
, CYRUS_SASLAUTHD_SOCKET
,
206 sizeof(srvaddr
.sun_path
));
207 r
= connect(s
, (struct sockaddr
*)&srvaddr
, sizeof(srvaddr
));
210 debug_printf("Cannot connect to saslauthd daemon (at '%s'): %s\n",
211 CYRUS_SASLAUTHD_SOCKET
, strerror(errno
));
212 *reply
= string_sprintf("cannot connect to saslauthd daemon at "
213 "%s: %s", CYRUS_SASLAUTHD_SOCKET
,
218 if ( write_string(s
, userid
, Ustrlen(userid
)) < 0) {
220 debug_printf("Failed to send userid to saslauthd daemon \n");
225 if ( write_string(s
, password
, Ustrlen(password
)) < 0) {
227 debug_printf("Failed to send password to saslauthd daemon \n");
232 memset((void *)password
, 0, Ustrlen(password
));
234 if ( write_string(s
, service
, Ustrlen(service
)) < 0) {
236 debug_printf("Failed to send service name to saslauthd daemon \n");
241 if ( write_string(s
, realm
, Ustrlen(realm
)) < 0) {
243 debug_printf("Failed to send realm to saslauthd daemon \n");
248 if ( read_string(s
, &daemon_reply
) < 2) {
250 debug_printf("Corrupted answer '%s' received. \n", daemon_reply
);
258 debug_printf("Answer '%s' received. \n", daemon_reply
);
260 *reply
= daemon_reply
;
262 if ( (daemon_reply
[0] == 'O') && (daemon_reply
[1] == 'K') )
265 if ( (daemon_reply
[0] == 'N') && (daemon_reply
[1] == 'O') )
274 /* helper functions */
275 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
277 #define MAX_REQ_LEN 1024
279 /* written from scratch */
281 /* FUNCTION: read_string */
284 * read a sasld-style counted string into
285 * store-allocated buffer, set pointer to the buffer,
286 * return number of bytes read or -1 on failure.
289 static int read_string(int fd
, uschar
**retval
) {
290 unsigned short count
;
293 rc
= (retry_read(fd
, &count
, sizeof(count
)) < (int) sizeof(count
));
295 count
= ntohs(count
);
296 if (count
> MAX_REQ_LEN
) {
299 *retval
= store_get(count
+ 1);
300 rc
= (retry_read(fd
, *retval
, count
) < (int) count
);
301 (*retval
)[count
] = '\0';
309 /* FUNCTION: write_string */
312 * write a sasld-style counted string into given fd
313 * written bytes on success, -1 on failure.
316 static int write_string(int fd
, const uschar
*string
, int len
) {
317 unsigned short count
;
323 iov
[0].iov_base
= (void *) &count
;
324 iov
[0].iov_len
= sizeof(count
);
325 iov
[1].iov_base
= (void *) string
;
326 iov
[1].iov_len
= len
;
328 rc
= retry_writev(fd
, iov
, 2);
334 /* taken from cyrus-sasl file saslauthd/saslauthd-unix.c */
336 /* FUNCTION: retry_read */
339 * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
340 * until all the data is read in or an error occurs.
342 static int retry_read(int fd
, void *inbuf
, unsigned nbyte
)
346 char *buf
= (char *)inbuf
;
348 if (nbyte
== 0) return 0;
351 n
= read(fd
, buf
, nbyte
);
357 if (errno
== EINTR
) continue;
363 if (n
>= (int) nbyte
) return nread
;
370 /* END FUNCTION: retry_read */
372 /* FUNCTION: retry_writev */
375 * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
376 * until all the data is written out or an error occurs.
379 static int /* R: bytes written, or -1 on error */
382 int fd
, /* I: fd to write on */
383 struct iovec
*iov
, /* U: iovec array base
384 * modified as data written */
385 int iovcnt
/* I: number of iovec entries */
390 int n
; /* return value from writev() */
391 int i
; /* loop counter */
392 int written
; /* bytes written so far */
393 static int iov_max
; /* max number of iovec entries */
402 # else /* ! IOV_MAX */
404 # endif /* ! IOV_MAX */
405 #endif /* ! MAXIOV */
410 while (iovcnt
&& iov
[0].iov_len
== 0) {
419 n
= writev(fd
, iov
, iovcnt
> iov_max
? iov_max
: iovcnt
);
421 if (errno
== EINVAL
&& iov_max
> 10) {
425 if (errno
== EINTR
) {
433 for (i
= 0; i
< iovcnt
; i
++) {
434 if (iov
[i
].iov_len
> (unsigned) n
) {
435 iov
[i
].iov_base
= (char *)iov
[i
].iov_base
+ n
;
450 /* END FUNCTION: retry_writev */
453 /* End of auths/pwcheck.c */