Remove attempts to quieten compiler static-checking
[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 *reply = "pwcheck support is not included in this Exim binary";
90 return PWCHECK_FAIL;
91 }
92
93
94 /* This is the real function */
95
96 #else
97
98  /* taken from cyrus-sasl file checkpw.c */
99  /* pwcheck daemon-authenticated login */
100  int pwcheck_verify_password(const char *userid,
101                                   const char *passwd,
102                                   const char **reply)
103  {
104      int s, start, r, n;
105      struct sockaddr_un srvaddr;
106      struct iovec iov[2];
107      static char response[1024];
108
109      *reply = NULL;
110
111      s = socket(AF_UNIX, SOCK_STREAM, 0);
112      if (s == -1) { return PWCHECK_FAIL; }
113
114      memset(CS &srvaddr, 0, sizeof(srvaddr));
115      srvaddr.sun_family = AF_UNIX;
116      strncpy(srvaddr.sun_path, CYRUS_PWCHECK_SOCKET, sizeof(srvaddr.sun_path));
117      r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
118      if (r == -1) {
119         DEBUG(D_auth)
120             debug_printf("Cannot connect to pwcheck daemon (at '%s')\n",CYRUS_PWCHECK_SOCKET);
121        *reply = "cannot connect to pwcheck daemon";
122        return PWCHECK_FAIL;
123      }
124
125      iov[0].iov_base = CS userid;
126      iov[0].iov_len = strlen(userid)+1;
127      iov[1].iov_base = CS passwd;
128      iov[1].iov_len = strlen(passwd)+1;
129
130      retry_writev(s, iov, 2);
131
132      start = 0;
133      while (start < sizeof(response) - 1) {
134        n = read(s, response+start, sizeof(response) - 1 - start);
135        if (n < 1) break;
136        start += n;
137      }
138
139      (void)close(s);
140
141      if (start > 1 && !strncmp(response, "OK", 2)) {
142        return PWCHECK_OK;
143      }
144
145      response[start] = '\0';
146      *reply = response;
147      return PWCHECK_NO;
148  }
149
150 #endif
151
152
153
154  /* A dummy function that always fails if saslauthd support is not
155 wanted. */
156
157 #ifndef CYRUS_SASLAUTHD_SOCKET
158 int saslauthd_verify_password(const uschar *userid,
159                 const uschar *passwd,
160                 const uschar *service,
161                 const uschar *realm,
162                 const uschar **reply)
163 {
164 *reply = US"saslauthd support is not included in this Exim binary";
165 return PWCHECK_FAIL;
166 }
167
168
169 /* This is the real function */
170
171 #else
172  /* written from scratch  */
173  /* saslauthd daemon-authenticated login */
174
175 int saslauthd_verify_password(const uschar *userid,
176                 const uschar *password,
177                 const uschar *service,
178                 const uschar *realm,
179                 const uschar **reply)
180 {
181     uschar *daemon_reply = NULL;
182     int s, r;
183     struct sockaddr_un srvaddr;
184
185     DEBUG(D_auth)
186        debug_printf("saslauthd userid='%s' servicename='%s'"
187                     " realm='%s'\n", userid, service, realm );
188
189     *reply = NULL;
190
191     s = socket(AF_UNIX, SOCK_STREAM, 0);
192     if (s == -1) {
193        *reply = CUstrerror(errno);
194        return PWCHECK_FAIL;
195     }
196
197     memset(CS &srvaddr, 0, sizeof(srvaddr));
198     srvaddr.sun_family = AF_UNIX;
199     strncpy(srvaddr.sun_path, CYRUS_SASLAUTHD_SOCKET,
200             sizeof(srvaddr.sun_path));
201     r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
202     if (r == -1) {
203        DEBUG(D_auth)
204             debug_printf("Cannot connect to saslauthd daemon (at '%s'): %s\n",
205                          CYRUS_SASLAUTHD_SOCKET, strerror(errno));
206        *reply = string_sprintf("cannot connect to saslauthd daemon at "
207                                "%s: %s", CYRUS_SASLAUTHD_SOCKET,
208                                strerror(errno));
209        return PWCHECK_FAIL;
210     }
211
212     if ( write_string(s, userid, Ustrlen(userid)) < 0) {
213         DEBUG(D_auth)
214             debug_printf("Failed to send userid to saslauthd daemon \n");
215         (void)close(s);
216         return PWCHECK_FAIL;
217     }
218
219     if ( write_string(s, password, Ustrlen(password)) < 0) {
220         DEBUG(D_auth)
221             debug_printf("Failed to send password to saslauthd daemon \n");
222         (void)close(s);
223         return PWCHECK_FAIL;
224     }
225
226     memset((void *)password, 0, Ustrlen(password));
227
228     if ( write_string(s, service, Ustrlen(service)) < 0) {
229         DEBUG(D_auth)
230             debug_printf("Failed to send service name to saslauthd daemon \n");
231         (void)close(s);
232         return PWCHECK_FAIL;
233     }
234
235     if ( write_string(s, realm, Ustrlen(realm)) < 0) {
236         DEBUG(D_auth)
237             debug_printf("Failed to send realm to saslauthd daemon \n");
238         (void)close(s);
239         return PWCHECK_FAIL;
240     }
241
242     if ( read_string(s, &daemon_reply ) < 2) {
243         DEBUG(D_auth)
244             debug_printf("Corrupted answer '%s' received. \n", daemon_reply);
245         (void)close(s);
246         return PWCHECK_FAIL;
247     }
248
249     (void)close(s);
250
251     DEBUG(D_auth)
252         debug_printf("Answer '%s' received. \n", daemon_reply);
253
254     *reply = daemon_reply;
255
256     if ( (daemon_reply[0] == 'O') && (daemon_reply[1] == 'K') )
257         return PWCHECK_OK;
258
259     if ( (daemon_reply[0] == 'N') && (daemon_reply[1] == 'O') )
260         return PWCHECK_NO;
261
262     return PWCHECK_FAIL;
263 }
264
265 #endif
266
267
268 /* helper functions */
269 #if defined(CYRUS_PWCHECK_SOCKET) || defined(CYRUS_SASLAUTHD_SOCKET)
270
271 #define MAX_REQ_LEN 1024
272
273 /* written from scratch */
274
275 /* FUNCTION: read_string */
276
277 /* SYNOPSIS
278  * read a sasld-style counted string into
279  * store-allocated buffer, set pointer to the buffer,
280  * return number of bytes read or -1 on failure.
281  * END SYNOPSIS */
282
283 static int read_string(int fd, uschar **retval) {
284     unsigned short count;
285     int rc;
286
287     rc = (retry_read(fd, &count, sizeof(count)) < (int) sizeof(count));
288     if (!rc) {
289         count = ntohs(count);
290         if (count > MAX_REQ_LEN) {
291             return -1;
292         } else {
293             /* Assume the file is trusted, so no tainting */
294             *retval = store_get(count + 1, FALSE);
295             rc = (retry_read(fd, *retval, count) < (int) count);
296             (*retval)[count] = '\0';
297             return count;
298         }
299     }
300     return -1;
301 }
302
303
304 /* FUNCTION: write_string */
305
306 /* SYNOPSIS
307  * write a sasld-style counted string into given fd
308  * written bytes on success, -1 on failure.
309  * END SYNOPSIS */
310
311 static int write_string(int fd, const uschar *string, int len) {
312     unsigned short count;
313     int rc;
314     struct iovec iov[2];
315
316     count = htons(len);
317
318     iov[0].iov_base = (void *) &count;
319     iov[0].iov_len = sizeof(count);
320     iov[1].iov_base = (void *) string;
321     iov[1].iov_len = len;
322
323     rc = retry_writev(fd, iov, 2);
324
325     return rc;
326 }
327
328
329 /* taken from cyrus-sasl file saslauthd/saslauthd-unix.c  */
330
331 /* FUNCTION: retry_read */
332
333 /* SYNOPSIS
334  * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
335  * until all the data is read in or an error occurs.
336  * END SYNOPSIS */
337 static int retry_read(int fd, void *inbuf, unsigned nbyte)
338 {
339     int n;
340     int nread = 0;
341     char *buf = CS inbuf;
342
343     if (nbyte == 0) return 0;
344
345     for (;;) {
346        n = read(fd, buf, nbyte);
347        if (n == 0) {
348            /* end of file */
349            return -1;
350        }
351        if (n == -1) {
352            if (errno == EINTR) continue;
353            return -1;
354        }
355
356        nread += n;
357
358        if (n >= (int) nbyte) return nread;
359
360        buf += n;
361        nbyte -= n;
362     }
363 }
364
365 /* END FUNCTION: retry_read */
366
367 /* FUNCTION: retry_writev */
368
369 /* SYNOPSIS
370  * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
371  * until all the data is written out or an error occurs.
372  * END SYNOPSIS */
373
374 static int     /* R: bytes written, or -1 on error */
375 retry_writev (
376   /* PARAMETERS */
377   int fd,                              /* I: fd to write on */
378   struct iovec *iov,                   /* U: iovec array base
379                                         *    modified as data written */
380   int iovcnt                           /* I: number of iovec entries */
381   /* END PARAMETERS */
382   )
383 {
384     /* VARIABLES */
385     int n;                             /* return value from writev() */
386     int i;                             /* loop counter */
387     int written;                       /* bytes written so far */
388     static int iov_max;                        /* max number of iovec entries */
389     /* END VARIABLES */
390
391     /* initialization */
392 #ifdef MAXIOV
393     iov_max = MAXIOV;
394 #else /* ! MAXIOV */
395 # ifdef IOV_MAX
396     iov_max = IOV_MAX;
397 # else /* ! IOV_MAX */
398     iov_max = 8192;
399 # endif /* ! IOV_MAX */
400 #endif /* ! MAXIOV */
401     written = 0;
402
403     for (;;) {
404
405        while (iovcnt && iov[0].iov_len == 0) {
406            iov++;
407            iovcnt--;
408        }
409
410        if (!iovcnt) {
411            return written;
412        }
413
414        n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
415        if (n == -1) {
416            if (errno == EINVAL && iov_max > 10) {
417                iov_max /= 2;
418                continue;
419            }
420            if (errno == EINTR) {
421                continue;
422            }
423            return -1;
424        } else {
425            written += n;
426        }
427
428        for (i = 0; i < iovcnt; i++) {
429            if (iov[i].iov_len > (unsigned) n) {
430                iov[i].iov_base = CS iov[i].iov_base + n;
431                iov[i].iov_len -= n;
432                break;
433            }
434            n -= iov[i].iov_len;
435            iov[i].iov_len = 0;
436        }
437
438        if (i == iovcnt) {
439            return written;
440        }
441     }
442     /* NOTREACHED */
443 }
444
445 /* END FUNCTION: retry_writev */
446 #endif
447
448 /* End of auths/pwcheck.c */