Docs: fix validation
[users/jgh/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 /*
7  * Copyright (c) 2001 Carnegie Mellon University.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
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
19  *    distribution.
20  *
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
27  *      5000 Forbes Avenue
28  *      Pittsburgh, PA  15213-3890
29  *      (412) 268-4387, fax: (412) 268-7395
30  *      tech-transfer@andrew.cmu.edu
31  *
32  * 4. Redistributions of any form whatsoever must retain the following
33  *    acknowledgment:
34  *    "This product includes software developed by Computing Services
35  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36  *
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.
44  */
45
46 /*
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.
54  *
55  * screwdriver@lxnt.info
56  *
57  */
58
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. */
63
64
65 #include "../exim.h"
66 #include "pwcheck.h"
67
68
69 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
70
71 #include <sys/uio.h>
72
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);
77
78 #endif
79
80
81 /* A dummy function that always fails if pwcheck support is not
82 wanted. */
83
84 #ifndef CYRUS_PWCHECK_SOCKET
85 int pwcheck_verify_password(const char *userid,
86                             const char *passwd,
87                             const char **reply)
88 {
89 userid = userid;  /* Keep picky compilers happy */
90 passwd = passwd;
91 *reply = "pwcheck support is not included in this Exim binary";
92 return PWCHECK_FAIL;
93 }
94
95
96 /* This is the real function */
97
98 #else
99
100  /* taken from cyrus-sasl file checkpw.c */
101  /* pwcheck daemon-authenticated login */
102  int pwcheck_verify_password(const char *userid,
103                                   const char *passwd,
104                                   const char **reply)
105  {
106      int s, start, r, n;
107      struct sockaddr_un srvaddr;
108      struct iovec iov[2];
109      static char response[1024];
110
111      *reply = NULL;
112
113      s = socket(AF_UNIX, SOCK_STREAM, 0);
114      if (s == -1) { return PWCHECK_FAIL; }
115
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));
120      if (r == -1) {
121         DEBUG(D_auth)
122             debug_printf("Cannot connect to pwcheck daemon (at '%s')\n",CYRUS_PWCHECK_SOCKET);
123        *reply = "cannot connect to pwcheck daemon";
124        return PWCHECK_FAIL;
125      }
126
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;
131
132      retry_writev(s, iov, 2);
133
134      start = 0;
135      while (start < sizeof(response) - 1) {
136        n = read(s, response+start, sizeof(response) - 1 - start);
137        if (n < 1) break;
138        start += n;
139      }
140
141      (void)close(s);
142
143      if (start > 1 && !strncmp(response, "OK", 2)) {
144        return PWCHECK_OK;
145      }
146
147      response[start] = '\0';
148      *reply = response;
149      return PWCHECK_NO;
150  }
151
152 #endif
153
154
155
156  /* A dummy function that always fails if saslauthd support is not
157 wanted. */
158
159 #ifndef CYRUS_SASLAUTHD_SOCKET
160 int saslauthd_verify_password(const uschar *userid,
161                 const uschar *passwd,
162                 const uschar *service,
163                 const uschar *realm,
164                 const uschar **reply)
165 {
166 userid = userid;  /* Keep picky compilers happy */
167 passwd = passwd;
168 service = service;
169 realm = realm;
170 *reply = US"saslauthd support is not included in this Exim binary";
171 return PWCHECK_FAIL;
172 }
173
174
175 /* This is the real function */
176
177 #else
178  /* written from scratch  */
179  /* saslauthd daemon-authenticated login */
180
181 int saslauthd_verify_password(const uschar *userid,
182                 const uschar *password,
183                 const uschar *service,
184                 const uschar *realm,
185                 const uschar **reply)
186 {
187     uschar *daemon_reply = NULL;
188     int s, r;
189     struct sockaddr_un srvaddr;
190
191     DEBUG(D_auth)
192        debug_printf("saslauthd userid='%s' servicename='%s'"
193                     " realm='%s'\n", userid, service, realm );
194
195     *reply = NULL;
196
197     s = socket(AF_UNIX, SOCK_STREAM, 0);
198     if (s == -1) {
199        *reply = CUstrerror(errno);
200        return PWCHECK_FAIL;
201     }
202
203     memset(CS &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));
208     if (r == -1) {
209        DEBUG(D_auth)
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,
214                                strerror(errno));
215        return PWCHECK_FAIL;
216     }
217
218     if ( write_string(s, userid, Ustrlen(userid)) < 0) {
219         DEBUG(D_auth)
220             debug_printf("Failed to send userid to saslauthd daemon \n");
221         (void)close(s);
222         return PWCHECK_FAIL;
223     }
224
225     if ( write_string(s, password, Ustrlen(password)) < 0) {
226         DEBUG(D_auth)
227             debug_printf("Failed to send password to saslauthd daemon \n");
228         (void)close(s);
229         return PWCHECK_FAIL;
230     }
231
232     memset((void *)password, 0, Ustrlen(password));
233
234     if ( write_string(s, service, Ustrlen(service)) < 0) {
235         DEBUG(D_auth)
236             debug_printf("Failed to send service name to saslauthd daemon \n");
237         (void)close(s);
238         return PWCHECK_FAIL;
239     }
240
241     if ( write_string(s, realm, Ustrlen(realm)) < 0) {
242         DEBUG(D_auth)
243             debug_printf("Failed to send realm to saslauthd daemon \n");
244         (void)close(s);
245         return PWCHECK_FAIL;
246     }
247
248     if ( read_string(s, &daemon_reply ) < 2) {
249         DEBUG(D_auth)
250             debug_printf("Corrupted answer '%s' received. \n", daemon_reply);
251         (void)close(s);
252         return PWCHECK_FAIL;
253     }
254
255     (void)close(s);
256
257     DEBUG(D_auth)
258         debug_printf("Answer '%s' received. \n", daemon_reply);
259
260     *reply = daemon_reply;
261
262     if ( (daemon_reply[0] == 'O') && (daemon_reply[1] == 'K') )
263         return PWCHECK_OK;
264
265     if ( (daemon_reply[0] == 'N') && (daemon_reply[1] == 'O') )
266         return PWCHECK_NO;
267
268     return PWCHECK_FAIL;
269 }
270
271 #endif
272
273
274 /* helper functions */
275 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
276
277 #define MAX_REQ_LEN 1024
278
279 /* written from scratch */
280
281 /* FUNCTION: read_string */
282
283 /* SYNOPSIS
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.
287  * END SYNOPSIS */
288
289 static int read_string(int fd, uschar **retval) {
290     unsigned short count;
291     int rc;
292
293     rc = (retry_read(fd, &count, sizeof(count)) < (int) sizeof(count));
294     if (!rc) {
295         count = ntohs(count);
296         if (count > MAX_REQ_LEN) {
297             return -1;
298         } else {
299             /* Assume the file is trusted, so no tainting */
300             *retval = store_get(count + 1, FALSE);
301             rc = (retry_read(fd, *retval, count) < (int) count);
302             (*retval)[count] = '\0';
303             return count;
304         }
305     }
306     return -1;
307 }
308
309
310 /* FUNCTION: write_string */
311
312 /* SYNOPSIS
313  * write a sasld-style counted string into given fd
314  * written bytes on success, -1 on failure.
315  * END SYNOPSIS */
316
317 static int write_string(int fd, const uschar *string, int len) {
318     unsigned short count;
319     int rc;
320     struct iovec iov[2];
321
322     count = htons(len);
323
324     iov[0].iov_base = (void *) &count;
325     iov[0].iov_len = sizeof(count);
326     iov[1].iov_base = (void *) string;
327     iov[1].iov_len = len;
328
329     rc = retry_writev(fd, iov, 2);
330
331     return rc;
332 }
333
334
335 /* taken from cyrus-sasl file saslauthd/saslauthd-unix.c  */
336
337 /* FUNCTION: retry_read */
338
339 /* SYNOPSIS
340  * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
341  * until all the data is read in or an error occurs.
342  * END SYNOPSIS */
343 static int retry_read(int fd, void *inbuf, unsigned nbyte)
344 {
345     int n;
346     int nread = 0;
347     char *buf = CS inbuf;
348
349     if (nbyte == 0) return 0;
350
351     for (;;) {
352        n = read(fd, buf, nbyte);
353        if (n == 0) {
354            /* end of file */
355            return -1;
356        }
357        if (n == -1) {
358            if (errno == EINTR) continue;
359            return -1;
360        }
361
362        nread += n;
363
364        if (n >= (int) nbyte) return nread;
365
366        buf += n;
367        nbyte -= n;
368     }
369 }
370
371 /* END FUNCTION: retry_read */
372
373 /* FUNCTION: retry_writev */
374
375 /* SYNOPSIS
376  * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
377  * until all the data is written out or an error occurs.
378  * END SYNOPSIS */
379
380 static int     /* R: bytes written, or -1 on error */
381 retry_writev (
382   /* PARAMETERS */
383   int fd,                              /* I: fd to write on */
384   struct iovec *iov,                   /* U: iovec array base
385                                         *    modified as data written */
386   int iovcnt                           /* I: number of iovec entries */
387   /* END PARAMETERS */
388   )
389 {
390     /* VARIABLES */
391     int n;                             /* return value from writev() */
392     int i;                             /* loop counter */
393     int written;                       /* bytes written so far */
394     static int iov_max;                        /* max number of iovec entries */
395     /* END VARIABLES */
396
397     /* initialization */
398 #ifdef MAXIOV
399     iov_max = MAXIOV;
400 #else /* ! MAXIOV */
401 # ifdef IOV_MAX
402     iov_max = IOV_MAX;
403 # else /* ! IOV_MAX */
404     iov_max = 8192;
405 # endif /* ! IOV_MAX */
406 #endif /* ! MAXIOV */
407     written = 0;
408
409     for (;;) {
410
411        while (iovcnt && iov[0].iov_len == 0) {
412            iov++;
413            iovcnt--;
414        }
415
416        if (!iovcnt) {
417            return written;
418        }
419
420        n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
421        if (n == -1) {
422            if (errno == EINVAL && iov_max > 10) {
423                iov_max /= 2;
424                continue;
425            }
426            if (errno == EINTR) {
427                continue;
428            }
429            return -1;
430        } else {
431            written += n;
432        }
433
434        for (i = 0; i < iovcnt; i++) {
435            if (iov[i].iov_len > (unsigned) n) {
436                iov[i].iov_base = CS iov[i].iov_base + n;
437                iov[i].iov_len -= n;
438                break;
439            }
440            n -= iov[i].iov_len;
441            iov[i].iov_len = 0;
442        }
443
444        if (i == iovcnt) {
445            return written;
446        }
447     }
448     /* NOTREACHED */
449 }
450
451 /* END FUNCTION: retry_writev */
452 #endif
453
454 /* End of auths/pwcheck.c */