Copyright updates:
[exim.git] / src / src / auths / pwcheck.c
1 /* SASL server API implementation
2  * Rob Siemborski
3  * Tim Martin
4  * $Id: checkpw.c,v 1.49 2002/03/07 19:14:04 ken3 Exp $
5  */
6 /* Copyright (c) The Exim Maintainers 2021 - 2022 */
7 /*
8  * Copyright (c) 2001 Carnegie Mellon University.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  *
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in
19  *    the documentation and/or other materials provided with the
20  *    distribution.
21  *
22  * 3. The name "Carnegie Mellon University" must not be used to
23  *    endorse or promote products derived from this software without
24  *    prior written permission. For permission or any other legal
25  *    details, please contact
26  *      Office of Technology Transfer
27  *      Carnegie Mellon University
28  *      5000 Forbes Avenue
29  *      Pittsburgh, PA  15213-3890
30  *      (412) 268-4387, fax: (412) 268-7395
31  *      tech-transfer@andrew.cmu.edu
32  *
33  * 4. Redistributions of any form whatsoever must retain the following
34  *    acknowledgment:
35  *    "This product includes software developed by Computing Services
36  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
37  *
38  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
39  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
40  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
41  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
42  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
43  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
44  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45  */
46
47 /*
48  * Taken from Cyrus-SASL library and adapted by Alexander S. Sabourenkov
49  * Oct 2001 - Apr 2002: Slightly modified by Philip Hazel.
50  * Aug 2003: new code for saslauthd from Alexander S. Sabourenkov incorporated
51  *           by Philip Hazel (minor mods to avoid compiler warnings)
52  * Oct 2006: (PH) removed redundant tests on "reply" being NULL - some were
53  *           missing, and confused someone who was using this code for some
54  *           other purpose. Here in Exim, "reply" is never NULL.
55  *
56  * screwdriver@lxnt.info
57  *
58  */
59
60 /* Originally this module supported only the pwcheck daemon, which is where its
61 name comes from. Nowadays it supports saslauthd as well; pwcheck is in fact
62 deprecated. The definitions of CYRUS_PWCHECK_SOCKET and CYRUS_SASLAUTHD_SOCKET
63 determine whether the facilities are actually supported or not. */
64
65
66 #include "../exim.h"
67 #include "pwcheck.h"
68
69
70 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
71
72 #include <sys/uio.h>
73
74 static int retry_read(int, void *, unsigned );
75 static int retry_writev(int, struct iovec *, int );
76 static int read_string(int, uschar **);
77 static int write_string(int, const uschar *, int);
78
79 #endif
80
81
82 /* A dummy function that always fails if pwcheck support is not
83 wanted. */
84
85 #ifndef CYRUS_PWCHECK_SOCKET
86 int pwcheck_verify_password(const char *userid,
87                             const char *passwd,
88                             const char **reply)
89 {
90 *reply = "pwcheck support is not included in this Exim binary";
91 return PWCHECK_FAIL;
92 }
93
94
95 /* This is the real function */
96
97 #else
98
99  /* taken from cyrus-sasl file checkpw.c */
100  /* pwcheck daemon-authenticated login */
101  int pwcheck_verify_password(const char *userid,
102                                   const char *passwd,
103                                   const char **reply)
104  {
105      int s, start, r, n;
106      struct sockaddr_un srvaddr;
107      struct iovec iov[2];
108      static char response[1024];
109
110      *reply = NULL;
111
112      s = socket(AF_UNIX, SOCK_STREAM, 0);
113      if (s == -1) { return PWCHECK_FAIL; }
114
115      memset(CS &srvaddr, 0, sizeof(srvaddr));
116      srvaddr.sun_family = AF_UNIX;
117      strncpy(srvaddr.sun_path, CYRUS_PWCHECK_SOCKET, sizeof(srvaddr.sun_path));
118      r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
119      if (r == -1) {
120         DEBUG(D_auth)
121             debug_printf("Cannot connect to pwcheck daemon (at '%s')\n",CYRUS_PWCHECK_SOCKET);
122        *reply = "cannot connect to pwcheck daemon";
123        return PWCHECK_FAIL;
124      }
125
126      iov[0].iov_base = CS userid;
127      iov[0].iov_len = strlen(userid)+1;
128      iov[1].iov_base = CS passwd;
129      iov[1].iov_len = strlen(passwd)+1;
130
131      retry_writev(s, iov, 2);
132
133      start = 0;
134      while (start < sizeof(response) - 1) {
135        n = read(s, response+start, sizeof(response) - 1 - start);
136        if (n < 1) break;
137        start += n;
138      }
139
140      (void)close(s);
141
142      if (start > 1 && !strncmp(response, "OK", 2)) {
143        return PWCHECK_OK;
144      }
145
146      response[start] = '\0';
147      *reply = response;
148      return PWCHECK_NO;
149  }
150
151 #endif
152
153
154
155  /* A dummy function that always fails if saslauthd support is not
156 wanted. */
157
158 #ifndef CYRUS_SASLAUTHD_SOCKET
159 int saslauthd_verify_password(const uschar *userid,
160                 const uschar *passwd,
161                 const uschar *service,
162                 const uschar *realm,
163                 const uschar **reply)
164 {
165 *reply = US"saslauthd support is not included in this Exim binary";
166 return PWCHECK_FAIL;
167 }
168
169
170 /* This is the real function */
171
172 #else
173  /* written from scratch  */
174  /* saslauthd daemon-authenticated login */
175
176 int saslauthd_verify_password(const uschar *userid,
177                 const uschar *password,
178                 const uschar *service,
179                 const uschar *realm,
180                 const uschar **reply)
181 {
182     uschar *daemon_reply = NULL;
183     int s, r;
184     struct sockaddr_un srvaddr;
185
186     DEBUG(D_auth)
187        debug_printf("saslauthd userid='%s' servicename='%s'"
188                     " realm='%s'\n", userid, service, realm );
189
190     *reply = NULL;
191
192     s = socket(AF_UNIX, SOCK_STREAM, 0);
193     if (s == -1) {
194        *reply = CUstrerror(errno);
195        return PWCHECK_FAIL;
196     }
197
198     memset(CS &srvaddr, 0, sizeof(srvaddr));
199     srvaddr.sun_family = AF_UNIX;
200     strncpy(srvaddr.sun_path, CYRUS_SASLAUTHD_SOCKET,
201             sizeof(srvaddr.sun_path));
202     r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
203     if (r == -1) {
204        DEBUG(D_auth)
205             debug_printf("Cannot connect to saslauthd daemon (at '%s'): %s\n",
206                          CYRUS_SASLAUTHD_SOCKET, strerror(errno));
207        *reply = string_sprintf("cannot connect to saslauthd daemon at "
208                                "%s: %s", CYRUS_SASLAUTHD_SOCKET,
209                                strerror(errno));
210        return PWCHECK_FAIL;
211     }
212
213     if ( write_string(s, userid, Ustrlen(userid)) < 0) {
214         DEBUG(D_auth)
215             debug_printf("Failed to send userid to saslauthd daemon \n");
216         (void)close(s);
217         return PWCHECK_FAIL;
218     }
219
220     if ( write_string(s, password, Ustrlen(password)) < 0) {
221         DEBUG(D_auth)
222             debug_printf("Failed to send password to saslauthd daemon \n");
223         (void)close(s);
224         return PWCHECK_FAIL;
225     }
226
227     memset((void *)password, 0, Ustrlen(password));
228
229     if ( write_string(s, service, Ustrlen(service)) < 0) {
230         DEBUG(D_auth)
231             debug_printf("Failed to send service name to saslauthd daemon \n");
232         (void)close(s);
233         return PWCHECK_FAIL;
234     }
235
236     if ( write_string(s, realm, Ustrlen(realm)) < 0) {
237         DEBUG(D_auth)
238             debug_printf("Failed to send realm to saslauthd daemon \n");
239         (void)close(s);
240         return PWCHECK_FAIL;
241     }
242
243     if ( read_string(s, &daemon_reply ) < 2) {
244         DEBUG(D_auth)
245             debug_printf("Corrupted answer '%s' received. \n", daemon_reply);
246         (void)close(s);
247         return PWCHECK_FAIL;
248     }
249
250     (void)close(s);
251
252     DEBUG(D_auth)
253         debug_printf("Answer '%s' received. \n", daemon_reply);
254
255     *reply = daemon_reply;
256
257     if ( (daemon_reply[0] == 'O') && (daemon_reply[1] == 'K') )
258         return PWCHECK_OK;
259
260     if ( (daemon_reply[0] == 'N') && (daemon_reply[1] == 'O') )
261         return PWCHECK_NO;
262
263     return PWCHECK_FAIL;
264 }
265
266 #endif
267
268
269 /* helper functions */
270 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
271
272 #define MAX_REQ_LEN 1024
273
274 /* written from scratch */
275
276 /* FUNCTION: read_string */
277
278 /* SYNOPSIS
279  * read a sasld-style counted string into
280  * store-allocated buffer, set pointer to the buffer,
281  * return number of bytes read or -1 on failure.
282  * END SYNOPSIS */
283
284 static int read_string(int fd, uschar **retval) {
285     unsigned short count;
286     int rc;
287
288     rc = (retry_read(fd, &count, sizeof(count)) < (int) sizeof(count));
289     if (!rc) {
290         count = ntohs(count);
291         if (count > MAX_REQ_LEN) {
292             return -1;
293         } else {
294             /* Assume the file is trusted, so no tainting */
295             *retval = store_get(count + 1, GET_UNTAINTED);
296             rc = (retry_read(fd, *retval, count) < (int) count);
297             (*retval)[count] = '\0';
298             return count;
299         }
300     }
301     return -1;
302 }
303
304
305 /* FUNCTION: write_string */
306
307 /* SYNOPSIS
308  * write a sasld-style counted string into given fd
309  * written bytes on success, -1 on failure.
310  * END SYNOPSIS */
311
312 static int write_string(int fd, const uschar *string, int len) {
313     unsigned short count;
314     int rc;
315     struct iovec iov[2];
316
317     count = htons(len);
318
319     iov[0].iov_base = (void *) &count;
320     iov[0].iov_len = sizeof(count);
321     iov[1].iov_base = (void *) string;
322     iov[1].iov_len = len;
323
324     rc = retry_writev(fd, iov, 2);
325
326     return rc;
327 }
328
329
330 /* taken from cyrus-sasl file saslauthd/saslauthd-unix.c  */
331
332 /* FUNCTION: retry_read */
333
334 /* SYNOPSIS
335  * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
336  * until all the data is read in or an error occurs.
337  * END SYNOPSIS */
338 static int retry_read(int fd, void *inbuf, unsigned nbyte)
339 {
340     int n;
341     int nread = 0;
342     char *buf = CS inbuf;
343
344     if (nbyte == 0) return 0;
345
346     for (;;) {
347        n = read(fd, buf, nbyte);
348        if (n == 0) {
349            /* end of file */
350            return -1;
351        }
352        if (n == -1) {
353            if (errno == EINTR) continue;
354            return -1;
355        }
356
357        nread += n;
358
359        if (n >= (int) nbyte) return nread;
360
361        buf += n;
362        nbyte -= n;
363     }
364 }
365
366 /* END FUNCTION: retry_read */
367
368 /* FUNCTION: retry_writev */
369
370 /* SYNOPSIS
371  * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
372  * until all the data is written out or an error occurs.
373  * END SYNOPSIS */
374
375 static int     /* R: bytes written, or -1 on error */
376 retry_writev (
377   /* PARAMETERS */
378   int fd,                              /* I: fd to write on */
379   struct iovec *iov,                   /* U: iovec array base
380                                         *    modified as data written */
381   int iovcnt                           /* I: number of iovec entries */
382   /* END PARAMETERS */
383   )
384 {
385     /* VARIABLES */
386     int n;                             /* return value from writev() */
387     int i;                             /* loop counter */
388     int written;                       /* bytes written so far */
389     static int iov_max;                        /* max number of iovec entries */
390     /* END VARIABLES */
391
392     /* initialization */
393 #ifdef MAXIOV
394     iov_max = MAXIOV;
395 #else /* ! MAXIOV */
396 # ifdef IOV_MAX
397     iov_max = IOV_MAX;
398 # else /* ! IOV_MAX */
399     iov_max = 8192;
400 # endif /* ! IOV_MAX */
401 #endif /* ! MAXIOV */
402     written = 0;
403
404     for (;;) {
405
406        while (iovcnt && iov[0].iov_len == 0) {
407            iov++;
408            iovcnt--;
409        }
410
411        if (!iovcnt) {
412            return written;
413        }
414
415        n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
416        if (n == -1) {
417            if (errno == EINVAL && iov_max > 10) {
418                iov_max /= 2;
419                continue;
420            }
421            if (errno == EINTR) {
422                continue;
423            }
424            return -1;
425        } else {
426            written += n;
427        }
428
429        for (i = 0; i < iovcnt; i++) {
430            if (iov[i].iov_len > (unsigned) n) {
431                iov[i].iov_base = CS iov[i].iov_base + n;
432                iov[i].iov_len -= n;
433                break;
434            }
435            n -= iov[i].iov_len;
436            iov[i].iov_len = 0;
437        }
438
439        if (i == iovcnt) {
440            return written;
441        }
442     }
443     /* NOTREACHED */
444 }
445
446 /* END FUNCTION: retry_writev */
447 #endif
448
449 /* End of auths/pwcheck.c */