Merge branch 'master' into 4.next
[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             *retval = store_get(count + 1);
300             rc = (retry_read(fd, *retval, count) < (int) count);
301             (*retval)[count] = '\0';
302             return count;
303         }
304     }
305     return -1;
306 }
307
308
309 /* FUNCTION: write_string */
310
311 /* SYNOPSIS
312  * write a sasld-style counted string into given fd
313  * written bytes on success, -1 on failure.
314  * END SYNOPSIS */
315
316 static int write_string(int fd, const uschar *string, int len) {
317     unsigned short count;
318     int rc;
319     struct iovec iov[2];
320
321     count = htons(len);
322
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;
327
328     rc = retry_writev(fd, iov, 2);
329
330     return rc;
331 }
332
333
334 /* taken from cyrus-sasl file saslauthd/saslauthd-unix.c  */
335
336 /* FUNCTION: retry_read */
337
338 /* SYNOPSIS
339  * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
340  * until all the data is read in or an error occurs.
341  * END SYNOPSIS */
342 static int retry_read(int fd, void *inbuf, unsigned nbyte)
343 {
344     int n;
345     int nread = 0;
346     char *buf = CS inbuf;
347
348     if (nbyte == 0) return 0;
349
350     for (;;) {
351        n = read(fd, buf, nbyte);
352        if (n == 0) {
353            /* end of file */
354            return -1;
355        }
356        if (n == -1) {
357            if (errno == EINTR) continue;
358            return -1;
359        }
360
361        nread += n;
362
363        if (n >= (int) nbyte) return nread;
364
365        buf += n;
366        nbyte -= n;
367     }
368 }
369
370 /* END FUNCTION: retry_read */
371
372 /* FUNCTION: retry_writev */
373
374 /* SYNOPSIS
375  * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
376  * until all the data is written out or an error occurs.
377  * END SYNOPSIS */
378
379 static int     /* R: bytes written, or -1 on error */
380 retry_writev (
381   /* PARAMETERS */
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 */
386   /* END PARAMETERS */
387   )
388 {
389     /* VARIABLES */
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 */
394     /* END VARIABLES */
395
396     /* initialization */
397 #ifdef MAXIOV
398     iov_max = MAXIOV;
399 #else /* ! MAXIOV */
400 # ifdef IOV_MAX
401     iov_max = IOV_MAX;
402 # else /* ! IOV_MAX */
403     iov_max = 8192;
404 # endif /* ! IOV_MAX */
405 #endif /* ! MAXIOV */
406     written = 0;
407
408     for (;;) {
409
410        while (iovcnt && iov[0].iov_len == 0) {
411            iov++;
412            iovcnt--;
413        }
414
415        if (!iovcnt) {
416            return written;
417        }
418
419        n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
420        if (n == -1) {
421            if (errno == EINVAL && iov_max > 10) {
422                iov_max /= 2;
423                continue;
424            }
425            if (errno == EINTR) {
426                continue;
427            }
428            return -1;
429        } else {
430            written += n;
431        }
432
433        for (i = 0; i < iovcnt; i++) {
434            if (iov[i].iov_len > (unsigned) n) {
435                iov[i].iov_base = CS iov[i].iov_base + n;
436                iov[i].iov_len -= n;
437                break;
438            }
439            n -= iov[i].iov_len;
440            iov[i].iov_len = 0;
441        }
442
443        if (i == iovcnt) {
444            return written;
445        }
446     }
447     /* NOTREACHED */
448 }
449
450 /* END FUNCTION: retry_writev */
451 #endif
452
453 /* End of auths/pwcheck.c */