Remove obsolete $Cambridge$ CVS revision strings.
[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((char *)&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 = (char *)userid;
128 iov[0].iov_len = strlen(userid)+1;
129 iov[1].iov_base = (char *)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((char *)&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 = (char *)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 = (char *)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 */