tidying
[exim.git] / src / src / auths / pwcheck.c
CommitLineData
0756eb3c
PH
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)
602e59e5
PH
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.
0756eb3c
PH
54 *
55 * screwdriver@lxnt.info
56 *
57 */
58
59/* Originally this module supported only the pwcheck daemon, which is where its
60name comes from. Nowadays it supports saslauthd as well; pwcheck is in fact
61deprecated. The definitions of CYRUS_PWCHECK_SOCKET and CYRUS_SASLAUTHD_SOCKET
62determine 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
73static int retry_read(int, void *, unsigned );
74static int retry_writev(int, struct iovec *, int );
75static int read_string(int, uschar **);
76static int write_string(int, const uschar *, int);
77
78#endif
79
80
81/* A dummy function that always fails if pwcheck support is not
82wanted. */
83
84#ifndef CYRUS_PWCHECK_SOCKET
85int pwcheck_verify_password(const char *userid,
86 const char *passwd,
87 const char **reply)
88{
89userid = userid; /* Keep picky compilers happy */
90passwd = passwd;
91*reply = "pwcheck support is not included in this Exim binary";
92return 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
602e59e5 111 *reply = NULL;
0756eb3c
PH
112
113 s = socket(AF_UNIX, SOCK_STREAM, 0);
114 if (s == -1) { return PWCHECK_FAIL; }
115
5903c6ff 116 memset(CS &srvaddr, 0, sizeof(srvaddr));
0756eb3c
PH
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);
602e59e5 123 *reply = "cannot connect to pwcheck daemon";
0756eb3c
PH
124 return PWCHECK_FAIL;
125 }
126
5903c6ff 127 iov[0].iov_base = CS userid;
0756eb3c 128 iov[0].iov_len = strlen(userid)+1;
5903c6ff 129 iov[1].iov_base = CS passwd;
0756eb3c
PH
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
f1e894f3 141 (void)close(s);
0756eb3c
PH
142
143 if (start > 1 && !strncmp(response, "OK", 2)) {
144 return PWCHECK_OK;
145 }
146
147 response[start] = '\0';
602e59e5 148 *reply = response;
0756eb3c
PH
149 return PWCHECK_NO;
150 }
151
152#endif
153
154
155
156 /* A dummy function that always fails if saslauthd support is not
157wanted. */
158
159#ifndef CYRUS_SASLAUTHD_SOCKET
160int saslauthd_verify_password(const uschar *userid,
161 const uschar *passwd,
162 const uschar *service,
163 const uschar *realm,
164 const uschar **reply)
165{
166userid = userid; /* Keep picky compilers happy */
167passwd = passwd;
168service = service;
169realm = realm;
170*reply = US"saslauthd support is not included in this Exim binary";
171return PWCHECK_FAIL;
172}
173
174
175/* This is the real function */
176
177#else
178 /* written from scratch */
179 /* saslauthd daemon-authenticated login */
180
181int saslauthd_verify_password(const uschar *userid,
182 const uschar *password,
183 const uschar *service,
184 const uschar *realm,
185 const uschar **reply)
186{
91ecef39 187 uschar *daemon_reply = NULL;
0756eb3c
PH
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
602e59e5 195 *reply = NULL;
0756eb3c
PH
196
197 s = socket(AF_UNIX, SOCK_STREAM, 0);
198 if (s == -1) {
602e59e5 199 *reply = CUstrerror(errno);
0756eb3c
PH
200 return PWCHECK_FAIL;
201 }
202
5903c6ff 203 memset(CS &srvaddr, 0, sizeof(srvaddr));
0756eb3c
PH
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) {
602e59e5 209 DEBUG(D_auth)
0756eb3c
PH
210 debug_printf("Cannot connect to saslauthd daemon (at '%s'): %s\n",
211 CYRUS_SASLAUTHD_SOCKET, strerror(errno));
602e59e5
PH
212 *reply = string_sprintf("cannot connect to saslauthd daemon at "
213 "%s: %s", CYRUS_SASLAUTHD_SOCKET,
214 strerror(errno));
0756eb3c
PH
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");
f1e894f3 221 (void)close(s);
0756eb3c
PH
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");
f1e894f3 228 (void)close(s);
0756eb3c
PH
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");
f1e894f3 237 (void)close(s);
0756eb3c
PH
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");
f1e894f3 244 (void)close(s);
0756eb3c
PH
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);
f1e894f3 251 (void)close(s);
0756eb3c
PH
252 return PWCHECK_FAIL;
253 }
254
f1e894f3 255 (void)close(s);
0756eb3c
PH
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
289static 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 {
f3ebb786
JH
299 /* Assume the file is trusted, so no tainting */
300 *retval = store_get(count + 1, FALSE);
0756eb3c
PH
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
317static 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 */
343static int retry_read(int fd, void *inbuf, unsigned nbyte)
344{
345 int n;
346 int nread = 0;
5903c6ff 347 char *buf = CS inbuf;
0756eb3c
PH
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
380static int /* R: bytes written, or -1 on error */
381retry_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) {
5903c6ff 436 iov[i].iov_base = CS iov[i].iov_base + n;
0756eb3c
PH
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 */