1 /* SASL server API implementation
4 * $Id: checkpw.c,v 1.49 2002/03/07 19:14:04 ken3 Exp $
6 /* Copyright (c) The Exim Maintainers 2021 - 2022 */
7 /* SPDX-License-Identifier: GPL-2.0-or-later */
9 * Copyright (c) 2001 Carnegie Mellon University. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in
20 * the documentation and/or other materials provided with the
23 * 3. The name "Carnegie Mellon University" must not be used to
24 * endorse or promote products derived from this software without
25 * prior written permission. For permission or any other legal
26 * details, please contact
27 * Office of Technology Transfer
28 * Carnegie Mellon University
30 * Pittsburgh, PA 15213-3890
31 * (412) 268-4387, fax: (412) 268-7395
32 * tech-transfer@andrew.cmu.edu
34 * 4. Redistributions of any form whatsoever must retain the following
36 * "This product includes software developed by Computing Services
37 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
39 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
40 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
41 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
42 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
43 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
44 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
45 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49 * Taken from Cyrus-SASL library and adapted by Alexander S. Sabourenkov
50 * Oct 2001 - Apr 2002: Slightly modified by Philip Hazel.
51 * Aug 2003: new code for saslauthd from Alexander S. Sabourenkov incorporated
52 * by Philip Hazel (minor mods to avoid compiler warnings)
53 * Oct 2006: (PH) removed redundant tests on "reply" being NULL - some were
54 * missing, and confused someone who was using this code for some
55 * other purpose. Here in Exim, "reply" is never NULL.
57 * screwdriver@lxnt.info
61 /* Originally this module supported only the pwcheck daemon, which is where its
62 name comes from. Nowadays it supports saslauthd as well; pwcheck is in fact
63 deprecated. The definitions of CYRUS_PWCHECK_SOCKET and CYRUS_SASLAUTHD_SOCKET
64 determine whether the facilities are actually supported or not. */
71 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
75 static int retry_read(int, void *, unsigned );
76 static int retry_writev(int, struct iovec *, int );
77 static int read_string(int, uschar **);
78 static int write_string(int, const uschar *, int);
83 /* A dummy function that always fails if pwcheck support is not
86 #ifndef CYRUS_PWCHECK_SOCKET
87 int pwcheck_verify_password(const char *userid,
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(CS &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 = CS userid;
128 iov[0].iov_len = strlen(userid)+1;
129 iov[1].iov_base = CS 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 *reply = US"saslauthd support is not included in this Exim binary";
171 /* This is the real function */
174 /* written from scratch */
175 /* saslauthd daemon-authenticated login */
177 int saslauthd_verify_password(const uschar *userid,
178 const uschar *password,
179 const uschar *service,
181 const uschar **reply)
183 uschar *daemon_reply = NULL;
185 struct sockaddr_un srvaddr;
188 debug_printf("saslauthd userid='%s' servicename='%s'"
189 " realm='%s'\n", userid, service, realm );
193 s = socket(AF_UNIX, SOCK_STREAM, 0);
195 *reply = CUstrerror(errno);
199 memset(CS &srvaddr, 0, sizeof(srvaddr));
200 srvaddr.sun_family = AF_UNIX;
201 strncpy(srvaddr.sun_path, CYRUS_SASLAUTHD_SOCKET,
202 sizeof(srvaddr.sun_path));
203 r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
206 debug_printf("Cannot connect to saslauthd daemon (at '%s'): %s\n",
207 CYRUS_SASLAUTHD_SOCKET, strerror(errno));
208 *reply = string_sprintf("cannot connect to saslauthd daemon at "
209 "%s: %s", CYRUS_SASLAUTHD_SOCKET,
214 if ( write_string(s, userid, Ustrlen(userid)) < 0) {
216 debug_printf("Failed to send userid to saslauthd daemon \n");
221 if ( write_string(s, password, Ustrlen(password)) < 0) {
223 debug_printf("Failed to send password to saslauthd daemon \n");
228 memset((void *)password, 0, Ustrlen(password));
230 if ( write_string(s, service, Ustrlen(service)) < 0) {
232 debug_printf("Failed to send service name to saslauthd daemon \n");
237 if ( write_string(s, realm, Ustrlen(realm)) < 0) {
239 debug_printf("Failed to send realm to saslauthd daemon \n");
244 if ( read_string(s, &daemon_reply ) < 2) {
246 debug_printf("Corrupted answer '%s' received. \n", daemon_reply);
254 debug_printf("Answer '%s' received. \n", daemon_reply);
256 *reply = daemon_reply;
258 if ( (daemon_reply[0] == 'O') && (daemon_reply[1] == 'K') )
261 if ( (daemon_reply[0] == 'N') && (daemon_reply[1] == 'O') )
270 /* helper functions */
271 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
273 #define MAX_REQ_LEN 1024
275 /* written from scratch */
277 /* FUNCTION: read_string */
280 * read a sasld-style counted string into
281 * store-allocated buffer, set pointer to the buffer,
282 * return number of bytes read or -1 on failure.
285 static int read_string(int fd, uschar **retval) {
286 unsigned short count;
289 rc = (retry_read(fd, &count, sizeof(count)) < (int) sizeof(count));
291 count = ntohs(count);
292 if (count > MAX_REQ_LEN) {
295 /* Assume the file is trusted, so no tainting */
296 *retval = store_get(count + 1, GET_UNTAINTED);
297 rc = (retry_read(fd, *retval, count) < (int) count);
298 (*retval)[count] = '\0';
306 /* FUNCTION: write_string */
309 * write a sasld-style counted string into given fd
310 * written bytes on success, -1 on failure.
313 static int write_string(int fd, const uschar *string, int len) {
314 unsigned short count;
320 iov[0].iov_base = (void *) &count;
321 iov[0].iov_len = sizeof(count);
322 iov[1].iov_base = (void *) string;
323 iov[1].iov_len = len;
325 rc = retry_writev(fd, iov, 2);
331 /* taken from cyrus-sasl file saslauthd/saslauthd-unix.c */
333 /* FUNCTION: retry_read */
336 * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
337 * until all the data is read in or an error occurs.
339 static int retry_read(int fd, void *inbuf, unsigned nbyte)
343 char *buf = CS inbuf;
345 if (nbyte == 0) return 0;
348 n = read(fd, buf, nbyte);
354 if (errno == EINTR) continue;
360 if (n >= (int) nbyte) return nread;
367 /* END FUNCTION: retry_read */
369 /* FUNCTION: retry_writev */
372 * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
373 * until all the data is written out or an error occurs.
376 static int /* R: bytes written, or -1 on error */
379 int fd, /* I: fd to write on */
380 struct iovec *iov, /* U: iovec array base
381 * modified as data written */
382 int iovcnt /* I: number of iovec entries */
387 int n; /* return value from writev() */
388 int i; /* loop counter */
389 int written; /* bytes written so far */
390 static int iov_max; /* max number of iovec entries */
399 # else /* ! IOV_MAX */
401 # endif /* ! IOV_MAX */
402 #endif /* ! MAXIOV */
407 while (iovcnt && iov[0].iov_len == 0) {
416 n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
418 if (errno == EINVAL && iov_max > 10) {
422 if (errno == EINTR) {
430 for (i = 0; i < iovcnt; i++) {
431 if (iov[i].iov_len > (unsigned) n) {
432 iov[i].iov_base = CS iov[i].iov_base + n;
447 /* END FUNCTION: retry_writev */
450 /* End of auths/pwcheck.c */