Alex Kiernan's patch for a problem with libradius.
[exim.git] / src / src / auths / pwcheck.c
1 /* $Cambridge: exim/src/src/auths/pwcheck.c,v 1.2 2005/06/27 14:29:44 ph10 Exp $ */
2
3 /* SASL server API implementation
4  * Rob Siemborski
5  * Tim Martin
6  * $Id: checkpw.c,v 1.49 2002/03/07 19:14:04 ken3 Exp $
7  */
8 /*
9  * Copyright (c) 2001 Carnegie Mellon University.  All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  *
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
21  *    distribution.
22  *
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
29  *      5000 Forbes Avenue
30  *      Pittsburgh, PA  15213-3890
31  *      (412) 268-4387, fax: (412) 268-7395
32  *      tech-transfer@andrew.cmu.edu
33  *
34  * 4. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by Computing Services
37  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
38  *
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.
46  */
47
48 /*
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  *
54  * screwdriver@lxnt.info
55  *
56  */
57
58 /* Originally this module supported only the pwcheck daemon, which is where its
59 name comes from. Nowadays it supports saslauthd as well; pwcheck is in fact
60 deprecated. The definitions of CYRUS_PWCHECK_SOCKET and CYRUS_SASLAUTHD_SOCKET
61 determine whether the facilities are actually supported or not. */
62
63
64 #include "../exim.h"
65 #include "pwcheck.h"
66
67
68 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
69
70 #include <sys/uio.h>
71
72 static int retry_read(int, void *, unsigned );
73 static int retry_writev(int, struct iovec *, int );
74 static int read_string(int, uschar **);
75 static int write_string(int, const uschar *, int);
76
77 #endif
78
79
80 /* A dummy function that always fails if pwcheck support is not
81 wanted. */
82
83 #ifndef CYRUS_PWCHECK_SOCKET
84 int pwcheck_verify_password(const char *userid,
85                             const char *passwd,
86                             const char **reply)
87 {
88 userid = userid;  /* Keep picky compilers happy */
89 passwd = passwd;
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      if (reply) { *reply = NULL; }
111
112      s = socket(AF_UNIX, SOCK_STREAM, 0);
113      if (s == -1) { return PWCHECK_FAIL; }
114
115      memset((char *)&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        if (reply) { *reply = "cannot connect to pwcheck daemon"; }
123        return PWCHECK_FAIL;
124      }
125
126      iov[0].iov_base = (char *)userid;
127      iov[0].iov_len = strlen(userid)+1;
128      iov[1].iov_base = (char *)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      if (reply) { *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 userid = userid;  /* Keep picky compilers happy */
166 passwd = passwd;
167 service = service;
168 realm = realm;
169 *reply = US"saslauthd support is not included in this Exim binary";
170 return PWCHECK_FAIL;
171 }
172
173
174 /* This is the real function */
175
176 #else
177  /* written from scratch  */
178  /* saslauthd daemon-authenticated login */
179
180 int saslauthd_verify_password(const uschar *userid,
181                 const uschar *password,
182                 const uschar *service,
183                 const uschar *realm,
184                 const uschar **reply)
185 {
186     uschar *daemon_reply;
187     int s, r;
188     struct sockaddr_un srvaddr;
189
190     DEBUG(D_auth)
191        debug_printf("saslauthd userid='%s' servicename='%s'"
192                     " realm='%s'\n", userid, service, realm );
193
194     if (reply)
195         *reply = NULL;
196
197     s = socket(AF_UNIX, SOCK_STREAM, 0);
198     if (s == -1) {
199         if (reply)
200             *reply = CUstrerror(errno);
201        return PWCHECK_FAIL;
202     }
203
204     memset((char *)&srvaddr, 0, sizeof(srvaddr));
205     srvaddr.sun_family = AF_UNIX;
206     strncpy(srvaddr.sun_path, CYRUS_SASLAUTHD_SOCKET,
207             sizeof(srvaddr.sun_path));
208     r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
209     if (r == -1) {
210         DEBUG(D_auth)
211             debug_printf("Cannot connect to saslauthd daemon (at '%s'): %s\n",
212                          CYRUS_SASLAUTHD_SOCKET, strerror(errno));
213        if (reply)
214            *reply = string_sprintf("cannot connect to saslauthd daemon at "
215                                    "%s: %s", CYRUS_SASLAUTHD_SOCKET,
216                                    strerror(errno));
217        return PWCHECK_FAIL;
218     }
219
220     if ( write_string(s, userid, Ustrlen(userid)) < 0) {
221         DEBUG(D_auth)
222             debug_printf("Failed to send userid to saslauthd daemon \n");
223         (void)close(s);
224         return PWCHECK_FAIL;
225     }
226
227     if ( write_string(s, password, Ustrlen(password)) < 0) {
228         DEBUG(D_auth)
229             debug_printf("Failed to send password to saslauthd daemon \n");
230         (void)close(s);
231         return PWCHECK_FAIL;
232     }
233
234     memset((void *)password, 0, Ustrlen(password));
235
236     if ( write_string(s, service, Ustrlen(service)) < 0) {
237         DEBUG(D_auth)
238             debug_printf("Failed to send service name to saslauthd daemon \n");
239         (void)close(s);
240         return PWCHECK_FAIL;
241     }
242
243     if ( write_string(s, realm, Ustrlen(realm)) < 0) {
244         DEBUG(D_auth)
245             debug_printf("Failed to send realm to saslauthd daemon \n");
246         (void)close(s);
247         return PWCHECK_FAIL;
248     }
249
250     if ( read_string(s, &daemon_reply ) < 2) {
251         DEBUG(D_auth)
252             debug_printf("Corrupted answer '%s' received. \n", daemon_reply);
253         (void)close(s);
254         return PWCHECK_FAIL;
255     }
256
257     (void)close(s);
258
259     DEBUG(D_auth)
260         debug_printf("Answer '%s' received. \n", daemon_reply);
261
262     *reply = daemon_reply;
263
264     if ( (daemon_reply[0] == 'O') && (daemon_reply[1] == 'K') )
265         return PWCHECK_OK;
266
267     if ( (daemon_reply[0] == 'N') && (daemon_reply[1] == 'O') )
268         return PWCHECK_NO;
269
270     return PWCHECK_FAIL;
271 }
272
273 #endif
274
275
276 /* helper functions */
277 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
278
279 #define MAX_REQ_LEN 1024
280
281 /* written from scratch */
282
283 /* FUNCTION: read_string */
284
285 /* SYNOPSIS
286  * read a sasld-style counted string into
287  * store-allocated buffer, set pointer to the buffer,
288  * return number of bytes read or -1 on failure.
289  * END SYNOPSIS */
290
291 static int read_string(int fd, uschar **retval) {
292     unsigned short count;
293     int rc;
294
295     rc = (retry_read(fd, &count, sizeof(count)) < (int) sizeof(count));
296     if (!rc) {
297         count = ntohs(count);
298         if (count > MAX_REQ_LEN) {
299             return -1;
300         } else {
301             *retval = store_get(count + 1);
302             rc = (retry_read(fd, *retval, count) < (int) count);
303             (*retval)[count] = '\0';
304             return count;
305         }
306     }
307     return -1;
308 }
309
310
311 /* FUNCTION: write_string */
312
313 /* SYNOPSIS
314  * write a sasld-style counted string into given fd
315  * written bytes on success, -1 on failure.
316  * END SYNOPSIS */
317
318 static int write_string(int fd, const uschar *string, int len) {
319     unsigned short count;
320     int rc;
321     struct iovec iov[2];
322
323     count = htons(len);
324
325     iov[0].iov_base = (void *) &count;
326     iov[0].iov_len = sizeof(count);
327     iov[1].iov_base = (void *) string;
328     iov[1].iov_len = len;
329
330     rc = retry_writev(fd, iov, 2);
331
332     return rc;
333 }
334
335
336 /* taken from cyrus-sasl file saslauthd/saslauthd-unix.c  */
337
338 /* FUNCTION: retry_read */
339
340 /* SYNOPSIS
341  * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
342  * until all the data is read in or an error occurs.
343  * END SYNOPSIS */
344 static int retry_read(int fd, void *inbuf, unsigned nbyte)
345 {
346     int n;
347     int nread = 0;
348     char *buf = (char *)inbuf;
349
350     if (nbyte == 0) return 0;
351
352     for (;;) {
353        n = read(fd, buf, nbyte);
354        if (n == 0) {
355            /* end of file */
356            return -1;
357        }
358        if (n == -1) {
359            if (errno == EINTR) continue;
360            return -1;
361        }
362
363        nread += n;
364
365        if (n >= (int) nbyte) return nread;
366
367        buf += n;
368        nbyte -= n;
369     }
370 }
371
372 /* END FUNCTION: retry_read */
373
374 /* FUNCTION: retry_writev */
375
376 /* SYNOPSIS
377  * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
378  * until all the data is written out or an error occurs.
379  * END SYNOPSIS */
380
381 static int     /* R: bytes written, or -1 on error */
382 retry_writev (
383   /* PARAMETERS */
384   int fd,                              /* I: fd to write on */
385   struct iovec *iov,                   /* U: iovec array base
386                                         *    modified as data written */
387   int iovcnt                           /* I: number of iovec entries */
388   /* END PARAMETERS */
389   )
390 {
391     /* VARIABLES */
392     int n;                             /* return value from writev() */
393     int i;                             /* loop counter */
394     int written;                       /* bytes written so far */
395     static int iov_max;                        /* max number of iovec entries */
396     /* END VARIABLES */
397
398     /* initialization */
399 #ifdef MAXIOV
400     iov_max = MAXIOV;
401 #else /* ! MAXIOV */
402 # ifdef IOV_MAX
403     iov_max = IOV_MAX;
404 # else /* ! IOV_MAX */
405     iov_max = 8192;
406 # endif /* ! IOV_MAX */
407 #endif /* ! MAXIOV */
408     written = 0;
409
410     for (;;) {
411
412        while (iovcnt && iov[0].iov_len == 0) {
413            iov++;
414            iovcnt--;
415        }
416
417        if (!iovcnt) {
418            return written;
419        }
420
421        n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
422        if (n == -1) {
423            if (errno == EINVAL && iov_max > 10) {
424                iov_max /= 2;
425                continue;
426            }
427            if (errno == EINTR) {
428                continue;
429            }
430            return -1;
431        } else {
432            written += n;
433        }
434
435        for (i = 0; i < iovcnt; i++) {
436            if (iov[i].iov_len > (unsigned) n) {
437                iov[i].iov_base = (char *)iov[i].iov_base + n;
438                iov[i].iov_len -= n;
439                break;
440            }
441            n -= iov[i].iov_len;
442            iov[i].iov_len = 0;
443        }
444
445        if (i == iovcnt) {
446            return written;
447        }
448     }
449     /* NOTREACHED */
450 }
451
452 /* END FUNCTION: retry_writev */
453 #endif
454
455 /* End of auths/pwcheck.c */