xref: /illumos-gate/usr/src/lib/libsasl/lib/checkpw.c (revision 1da57d55)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /* SASL server API implementation
7  * Rob Siemborski
8  * Tim Martin
9  * $Id: checkpw.c,v 1.62 2003/03/19 18:25:27 rjs3 Exp $
10  */
11 /*
12  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  *
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  *
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in
23  *    the documentation and/or other materials provided with the
24  *    distribution.
25  *
26  * 3. The name "Carnegie Mellon University" must not be used to
27  *    endorse or promote products derived from this software without
28  *    prior written permission. For permission or any other legal
29  *    details, please contact
30  *      Office of Technology Transfer
31  *      Carnegie Mellon University
32  *      5000 Forbes Avenue
33  *      Pittsburgh, PA  15213-3890
34  *      (412) 268-4387, fax: (412) 268-7395
35  *      tech-transfer@andrew.cmu.edu
36  *
37  * 4. Redistributions of any form whatsoever must retain the following
38  *    acknowledgment:
39  *    "This product includes software developed by Computing Services
40  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
41  *
42  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
43  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
44  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
45  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
46  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
47  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
48  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
49  */
50 
51 #include <config.h>
52 
53 /* checkpw stuff */
54 
55 #include <stdio.h>
56 #include "sasl.h"
57 #include "saslutil.h"
58 #include "saslplug.h"
59 #include "saslint.h"
60 
61 #include <assert.h>
62 #ifdef HAVE_UNISTD_H
63 #include <unistd.h>
64 #endif
65 #include <fcntl.h>
66 #ifdef USE_DOORS
67 #include <sys/mman.h>
68 #include <door.h>
69 #endif
70 
71 #include <stdlib.h>
72 
73 #ifndef WIN32
74 #include <strings.h>
75 #include <netdb.h>
76 #include <netinet/in.h>
77 #include <sys/un.h>
78 #else
79 #include <string.h>
80 #endif
81 
82 #include <sys/types.h>
83 #include <ctype.h>
84 
85 #ifdef HAVE_PWD_H
86 #include <pwd.h>
87 #endif /* HAVE_PWD_H */
88 #ifdef HAVE_SHADOW_H
89 #include <shadow.h>
90 #endif /* HAVE_SHADOW_H */
91 
92 #if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD)
93 # include <errno.h>
94 # include <sys/types.h>
95 # include <sys/socket.h>
96 # include <sys/un.h>
97 # ifdef HAVE_UNISTD_H
98 #  include <unistd.h>
99 # endif
100 
101 extern int errno;
102 #endif
103 
104 
105 /* we store the following secret to check plaintext passwords:
106  *
107  * <salt> \0 <secret>
108  *
109  * where <secret> = MD5(<salt>, "sasldb", <pass>)
110  */
111 #ifdef _SUN_SDK_
_sasl_make_plain_secret(const sasl_utils_t * utils,const char * salt,const char * passwd,size_t passlen,sasl_secret_t ** secret)112 static int _sasl_make_plain_secret(const sasl_utils_t *utils, const char *salt,
113 				const char *passwd, size_t passlen,
114 				sasl_secret_t **secret)
115 #else
116 static int _sasl_make_plain_secret(const char *salt,
117 				   const char *passwd, size_t passlen,
118 				   sasl_secret_t **secret)
119 #endif /* _SUN_SDK_ */
120 {
121     MD5_CTX ctx;
122     unsigned sec_len = 16 + 1 + 16; /* salt + "\0" + hash */
123 
124 #ifdef _SUN_SDK_
125     *secret = (sasl_secret_t *)utils->malloc(sizeof(sasl_secret_t) +
126 					sec_len * sizeof(char));
127 #else
128     *secret = (sasl_secret_t *) sasl_ALLOC(sizeof(sasl_secret_t) +
129 					   sec_len * sizeof(char));
130 #endif /* _SUN_SDK_ */
131     if (*secret == NULL) {
132 	return SASL_NOMEM;
133     }
134 
135     _sasl_MD5Init(&ctx);
136     _sasl_MD5Update(&ctx, salt, 16);
137     _sasl_MD5Update(&ctx, "sasldb", 6);
138     _sasl_MD5Update(&ctx, passwd, passlen);
139     memcpy((*secret)->data, salt, 16);
140     (*secret)->data[16] = '\0';
141     _sasl_MD5Final((*secret)->data + 17, &ctx);
142     (*secret)->len = sec_len;
143 
144     return SASL_OK;
145 }
146 
147 /* erase & dispose of a sasl_secret_t
148  */
auxprop_verify_password(sasl_conn_t * conn,const char * userstr,const char * passwd,const char * service,const char * user_realm)149 static int auxprop_verify_password(sasl_conn_t *conn,
150 				   const char *userstr,
151 				   const char *passwd,
152 				   const char *service __attribute__((unused)),
153 				   const char *user_realm __attribute__((unused)))
154 {
155     int ret = SASL_FAIL;
156     char *userid = NULL;
157 #ifndef _SUN_SDK_
158     char *realm = NULL;
159 #endif /* !_SUN_SDK_ */
160     int result = SASL_OK;
161     sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
162     const char *password_request[] = { SASL_AUX_PASSWORD,
163 				       "*cmusaslsecretPLAIN",
164 				       NULL };
165     struct propval auxprop_values[3];
166 
167     if (!conn || !userstr)
168 	return SASL_BADPARAM;
169 
170     /* We need to clear any previous results and re-canonify to
171      * ensure correctness */
172 
173     prop_clear(sconn->sparams->propctx, 0);
174 
175     /* ensure its requested */
176     result = prop_request(sconn->sparams->propctx, password_request);
177 
178     if(result != SASL_OK) return result;
179 
180     result = _sasl_canon_user(conn, userstr, 0,
181 			      SASL_CU_AUTHID | SASL_CU_AUTHZID,
182 			      &(conn->oparams));
183     if(result != SASL_OK) return result;
184 
185     result = prop_getnames(sconn->sparams->propctx, password_request,
186 			   auxprop_values);
187     if(result < 0)
188 	return result;
189 
190     if((!auxprop_values[0].name
191          || !auxprop_values[0].values || !auxprop_values[0].values[0])
192        && (!auxprop_values[1].name
193          || !auxprop_values[1].values || !auxprop_values[1].values[0]))
194 	    return SASL_NOUSER;
195 
196     /* It is possible for us to get useful information out of just
197      * the lookup, so we won't check that we have a password until now */
198     if(!passwd) {
199 	ret = SASL_BADPARAM;
200 	goto done;
201     }
202 
203     /* At the point this has been called, the username has been canonified
204      * and we've done the auxprop lookup.  This should be easy. */
205     if(auxprop_values[0].name
206        && auxprop_values[0].values
207        && auxprop_values[0].values[0]
208        && !strcmp(auxprop_values[0].values[0], passwd)) {
209 	/* We have a plaintext version and it matched! */
210 	return SASL_OK;
211     } else if(auxprop_values[1].name
212 	      && auxprop_values[1].values
213 	      && auxprop_values[1].values[0]) {
214 	const char *db_secret = auxprop_values[1].values[0];
215 	sasl_secret_t *construct;
216 
217 #ifdef _SUN_SDK_
218 	ret = _sasl_make_plain_secret(sconn->sparams->utils, db_secret,
219 				passwd, strlen(passwd),
220 				&construct);
221 #else
222 	ret = _sasl_make_plain_secret(db_secret, passwd,
223 				      strlen(passwd),
224 				      &construct);
225 #endif /* _SUN_SDK_ */
226 	if (ret != SASL_OK) {
227 	    goto done;
228 	}
229 
230 	if (!memcmp(db_secret, construct->data, construct->len)) {
231 	    /* password verified! */
232 	    ret = SASL_OK;
233 	} else {
234 	    /* passwords do not match */
235 	    ret = SASL_BADAUTH;
236 	}
237 
238 #ifdef _SUN_SDK_
239 	sconn->sparams->utils->free(construct);
240 #else
241 	sasl_FREE(construct);
242 #endif /* _SUN_SDK_ */
243     } else {
244 	/* passwords do not match */
245 	ret = SASL_BADAUTH;
246     }
247 
248  done:
249 #ifdef _SUN_SDK_
250     if (userid) sconn->sparams->utils->free(userid);
251 #else
252     if (userid) sasl_FREE(userid);
253     if (realm)  sasl_FREE(realm);
254 #endif /* _SUN_SDK_ */
255 
256     /* We're not going to erase the property here because other people
257      * may want it */
258     return ret;
259 }
260 
261 #ifdef DO_SASL_CHECKAPOP
_sasl_auxprop_verify_apop(sasl_conn_t * conn,const char * userstr,const char * challenge,const char * response,const char * user_realm)262 int _sasl_auxprop_verify_apop(sasl_conn_t *conn,
263 			      const char *userstr,
264 			      const char *challenge,
265 			      const char *response,
266 			      const char *user_realm __attribute__((unused)))
267 {
268     int ret = SASL_BADAUTH;
269     char *userid = NULL;
270 #ifndef _SUN_SDK_
271     char *realm = NULL;
272 #endif /* !_SUN_SDK_ */
273     unsigned char digest[16];
274     char digeststr[33];
275     const char *password_request[] = { SASL_AUX_PASSWORD, NULL };
276     struct propval auxprop_values[2];
277     sasl_server_conn_t *sconn = (sasl_server_conn_t *)conn;
278     MD5_CTX ctx;
279     int i;
280 
281     if (!conn || !userstr || !challenge || !response)
282        PARAMERROR(conn)
283 
284     /* We've done the auxprop lookup already (in our caller) */
285     /* sadly, APOP has no provision for storing secrets */
286     ret = prop_getnames(sconn->sparams->propctx, password_request,
287 			auxprop_values);
288     if(ret < 0) {
289 #ifdef _SUN_SDK_
290 	_sasl_log(conn, SASL_LOG_ERR, "could not perform password lookup");
291 #else
292 	sasl_seterror(conn, 0, "could not perform password lookup");
293 #endif /* _SUN_SDK_ */
294 	goto done;
295     }
296 
297     if(!auxprop_values[0].name ||
298        !auxprop_values[0].values ||
299        !auxprop_values[0].values[0]) {
300 #ifdef _INTEGRATED_SOLARIS_
301 	sasl_seterror(conn, 0, gettext("could not find password"));
302 #else
303 	sasl_seterror(conn, 0, "could not find password");
304 #endif /* _INTEGRATED_SOLARIS_ */
305 	ret = SASL_NOUSER;
306 	goto done;
307     }
308 
309     _sasl_MD5Init(&ctx);
310     _sasl_MD5Update(&ctx, challenge, strlen(challenge));
311     _sasl_MD5Update(&ctx, auxprop_values[0].values[0],
312 		    strlen(auxprop_values[0].values[0]));
313     _sasl_MD5Final(digest, &ctx);
314 
315     /* convert digest from binary to ASCII hex */
316     for (i = 0; i < 16; i++)
317       sprintf(digeststr + (i*2), "%02x", digest[i]);
318 
319     if (!strncasecmp(digeststr, response, 32)) {
320       /* password verified! */
321       ret = SASL_OK;
322     } else {
323       /* passwords do not match */
324       ret = SASL_BADAUTH;
325     }
326 
327  done:
328 #ifdef _INTEGRATED_SOLARIS_
329     if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG,
330 					   gettext("login incorrect"));
331 #else
332     if (ret == SASL_BADAUTH) sasl_seterror(conn, SASL_NOLOG,
333 					   "login incorrect");
334 #endif /* _INTEGRATED_SOLARIS_ */
335 #ifdef _SUN_SDK_
336     if (userid) sconn->sparams->utils->free(userid);
337 #else
338     if (userid) sasl_FREE(userid);
339     if (realm)  sasl_FREE(realm);
340 #endif /* _SUN_SDK_ */
341 
342     return ret;
343 }
344 #endif /* DO_SASL_CHECKAPOP */
345 
346 #if defined(HAVE_PWCHECK) || defined(HAVE_SASLAUTHD)
347 /*
348  * Keep calling the writev() system call with 'fd', 'iov', and 'iovcnt'
349  * until all the data is written out or an error occurs.
350  */
retry_writev(int fd,struct iovec * iov,int iovcnt)351 static int retry_writev(int fd, struct iovec *iov, int iovcnt)
352 {
353     int n;
354     int i;
355     int written = 0;
356     static int iov_max =
357 #ifdef MAXIOV
358 	MAXIOV
359 #else
360 #ifdef IOV_MAX
361 	IOV_MAX
362 #else
363 	8192
364 #endif
365 #endif
366 	;
367 
368     for (;;) {
369 	while (iovcnt && iov[0].iov_len == 0) {
370 	    iov++;
371 	    iovcnt--;
372 	}
373 
374 	if (!iovcnt) return written;
375 
376 	n = writev(fd, iov, iovcnt > iov_max ? iov_max : iovcnt);
377 	if (n == -1) {
378 	    if (errno == EINVAL && iov_max > 10) {
379 		iov_max /= 2;
380 		continue;
381 	    }
382 	    if (errno == EINTR) continue;
383 	    return -1;
384 	}
385 
386 	written += n;
387 
388 	for (i = 0; i < iovcnt; i++) {
389 	    if (iov[i].iov_len > (unsigned) n) {
390 		iov[i].iov_base = (char *)iov[i].iov_base + n;
391 		iov[i].iov_len -= n;
392 		break;
393 	    }
394 	    n -= iov[i].iov_len;
395 	    iov[i].iov_len = 0;
396 	}
397 
398 	if (i == iovcnt) return written;
399     }
400 }
401 
402 #endif
403 
404 #ifdef HAVE_PWCHECK
405 /* pwcheck daemon-authenticated login */
pwcheck_verify_password(sasl_conn_t * conn,const char * userid,const char * passwd,const char * service,const char * user_realm)406 static int pwcheck_verify_password(sasl_conn_t *conn,
407 				   const char *userid,
408 				   const char *passwd,
409 				   const char *service __attribute__((unused)),
410 				   const char *user_realm
411 				               __attribute__((unused)))
412 {
413     int s;
414     struct sockaddr_un srvaddr;
415     int r;
416     struct iovec iov[10];
417     static char response[1024];
418     unsigned start, n;
419     char pwpath[1024];
420 
421     if (strlen(PWCHECKDIR)+8+1 > sizeof(pwpath)) return SASL_FAIL;
422 
423     strcpy(pwpath, PWCHECKDIR);
424     strcat(pwpath, "/pwcheck");
425 
426     s = socket(AF_UNIX, SOCK_STREAM, 0);
427     if (s == -1) return errno;
428 
429     memset((char *)&srvaddr, 0, sizeof(srvaddr));
430     srvaddr.sun_family = AF_UNIX;
431     strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
432     r = connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr));
433     if (r == -1) {
434 	sasl_seterror(conn,0,"cannot connect to pwcheck server");
435 	return SASL_FAIL;
436     }
437 
438     iov[0].iov_base = (char *)userid;
439     iov[0].iov_len = strlen(userid)+1;
440     iov[1].iov_base = (char *)passwd;
441     iov[1].iov_len = strlen(passwd)+1;
442 
443     retry_writev(s, iov, 2);
444 
445     start = 0;
446     while (start < sizeof(response) - 1) {
447 	n = read(s, response+start, sizeof(response) - 1 - start);
448 	if (n < 1) break;
449 	start += n;
450     }
451 
452     close(s);
453 
454     if (start > 1 && !strncmp(response, "OK", 2)) {
455 	return SASL_OK;
456     }
457 
458     response[start] = '\0';
459     sasl_seterror(conn,0,response);
460     return SASL_BADAUTH;
461 }
462 
463 #endif
464 
465 #ifdef HAVE_SASLAUTHD
466 
467 /*
468  * Keep calling the read() system call with 'fd', 'buf', and 'nbyte'
469  * until all the data is read in or an error occurs.
470  */
retry_read(int fd,void * buf0,unsigned nbyte)471 static int retry_read(int fd, void *buf0, unsigned nbyte)
472 {
473     int n;
474     int nread = 0;
475     char *buf = buf0;
476 
477     if (nbyte == 0) return 0;
478 
479     for (;;) {
480 	n = read(fd, buf, nbyte);
481 	if (n == -1 || n == 0) {
482 	    if (errno == EINTR || errno == EAGAIN) continue;
483 	    return -1;
484 	}
485 
486 	nread += n;
487 
488 	if (nread >= (int) nbyte) return nread;
489 
490 	buf += n;
491 	nbyte -= n;
492     }
493 }
494 
495 /* saslauthd-authenticated login */
saslauthd_verify_password(sasl_conn_t * conn,const char * userid,const char * passwd,const char * service,const char * user_realm)496 static int saslauthd_verify_password(sasl_conn_t *conn,
497 				     const char *userid,
498 				     const char *passwd,
499 				     const char *service,
500 				     const char *user_realm)
501 {
502     char response[1024];
503     char query[8192];
504     char *query_end = query;
505     int s;
506     struct sockaddr_un srvaddr;
507     sasl_getopt_t *getopt;
508     void *context;
509     char pwpath[sizeof(srvaddr.sun_path)];
510     const char *p = NULL;
511 #ifdef USE_DOORS
512     door_arg_t arg;
513 #endif
514 
515     /* check to see if the user configured a rundir */
516     if (_sasl_getcallback(conn, SASL_CB_GETOPT, &getopt, &context) == SASL_OK) {
517 	getopt(context, NULL, "saslauthd_path", &p, NULL);
518     }
519     if (p) {
520 	strncpy(pwpath, p, sizeof(pwpath));
521     } else {
522 	if (strlen(PATH_SASLAUTHD_RUNDIR) + 4 + 1 > sizeof(pwpath))
523 	    return SASL_FAIL;
524 
525 	strcpy(pwpath, PATH_SASLAUTHD_RUNDIR);
526 	strcat(pwpath, "/mux");
527     }
528 
529     /*
530      * build request of the form:
531      *
532      * count authid count password count service count realm
533      */
534     {
535  	unsigned short u_len, p_len, s_len, r_len;
536 
537  	u_len = (strlen(userid));
538  	p_len = (strlen(passwd));
539 	s_len = (strlen(service));
540 	r_len = ((user_realm ? strlen(user_realm) : 0));
541 
542 	if (u_len + p_len + s_len + r_len + 30 > (unsigned short) sizeof(query)) {
543 	    /* request just too damn big */
544             sasl_seterror(conn, 0, "saslauthd request too large");
545 	    return SASL_FAIL;
546 	}
547 
548 	u_len = htons(u_len);
549 	p_len = htons(p_len);
550 	s_len = htons(s_len);
551 	r_len = htons(r_len);
552 
553 	memcpy(query_end, &u_len, sizeof(unsigned short));
554 	query_end += sizeof(unsigned short);
555 	while (*userid) *query_end++ = *userid++;
556 
557 	memcpy(query_end, &p_len, sizeof(unsigned short));
558 	query_end += sizeof(unsigned short);
559 	while (*passwd) *query_end++ = *passwd++;
560 
561 	memcpy(query_end, &s_len, sizeof(unsigned short));
562 	query_end += sizeof(unsigned short);
563 	while (*service) *query_end++ = *service++;
564 
565 	memcpy(query_end, &r_len, sizeof(unsigned short));
566 	query_end += sizeof(unsigned short);
567 	if (user_realm) while (*user_realm) *query_end++ = *user_realm++;
568     }
569 
570 #ifdef USE_DOORS
571     s = open(pwpath, O_RDONLY);
572     if (s < 0) {
573 	sasl_seterror(conn, 0, "cannot open door to saslauthd server: %m", errno);
574 	return SASL_FAIL;
575     }
576 
577     arg.data_ptr = query;
578     arg.data_size = query_end - query;
579     arg.desc_ptr = NULL;
580     arg.desc_num = 0;
581     arg.rbuf = response;
582     arg.rsize = sizeof(response);
583 
584     door_call(s, &arg);
585 
586     if (arg.data_ptr != response || arg.data_size >= sizeof(response)) {
587 	/* oh damn, we got back a really long response */
588 	munmap(arg.rbuf, arg.rsize);
589 	sasl_seterror(conn, 0, "saslauthd sent an overly long response");
590 	return SASL_FAIL;
591     }
592     response[arg.data_size] = '\0';
593 
594     close(s);
595 #else
596     /* unix sockets */
597 
598     s = socket(AF_UNIX, SOCK_STREAM, 0);
599     if (s == -1) {
600 	sasl_seterror(conn, 0, "cannot create socket for saslauthd: %m", errno);
601 	return SASL_FAIL;
602     }
603 
604     memset((char *)&srvaddr, 0, sizeof(srvaddr));
605     srvaddr.sun_family = AF_UNIX;
606     strncpy(srvaddr.sun_path, pwpath, sizeof(srvaddr.sun_path));
607 
608     {
609 	int r = connect(s, (struct sockaddr *) &srvaddr, sizeof(srvaddr));
610 	if (r == -1) {
611 	    sasl_seterror(conn, 0, "cannot connect to saslauthd server: %m", errno);
612 	    return SASL_FAIL;
613 	}
614     }
615 
616     {
617  	struct iovec iov[8];
618 
619 	iov[0].iov_len = query_end - query;
620 	iov[0].iov_base = query;
621 
622 	if (retry_writev(s, iov, 1) == -1) {
623             sasl_seterror(conn, 0, "write failed");
624   	    return SASL_FAIL;
625   	}
626     }
627 
628     {
629 	unsigned short count = 0;
630 
631 	/*
632 	 * read response of the form:
633 	 *
634 	 * count result
635 	 */
636 	if (retry_read(s, &count, sizeof(count)) < (int) sizeof(count)) {
637 	    sasl_seterror(conn, 0, "size read failed");
638 	    return SASL_FAIL;
639 	}
640 
641 	count = ntohs(count);
642 	if (count < 2) { /* MUST have at least "OK" or "NO" */
643 	    close(s);
644 	    sasl_seterror(conn, 0, "bad response from saslauthd");
645 	    return SASL_FAIL;
646 	}
647 
648 	count = (int)sizeof(response) < count ? sizeof(response) : count;
649 	if (retry_read(s, response, count) < count) {
650 	    close(s);
651 	    sasl_seterror(conn, 0, "read failed");
652 	    return SASL_FAIL;
653 	}
654 	response[count] = '\0';
655     }
656 
657     close(s);
658 #endif /* USE_DOORS */
659 
660     if (!strncmp(response, "OK", 2)) {
661 	return SASL_OK;
662     }
663 
664     sasl_seterror(conn, SASL_NOLOG, "authentication failed");
665     return SASL_BADAUTH;
666 }
667 
668 #endif
669 
670 #ifdef HAVE_ALWAYSTRUE
always_true(sasl_conn_t * conn,const char * userstr,const char * passwd,const char * service,const char * user_realm)671 static int always_true(sasl_conn_t *conn,
672 		       const char *userstr,
673 		       const char *passwd __attribute__((unused)),
674 		       const char *service __attribute__((unused)),
675 		       const char *user_realm __attribute__((unused)))
676 {
677     _sasl_log(conn, SASL_LOG_WARN, "AlwaysTrue Password Verifier Verified: %s",
678 	      userstr);
679     return SASL_OK;
680 }
681 #endif
682 
683 struct sasl_verify_password_s _sasl_verify_password[] = {
684     { "auxprop", &auxprop_verify_password },
685 #ifdef HAVE_PWCHECK
686     { "pwcheck", &pwcheck_verify_password },
687 #endif
688 #ifdef HAVE_SASLAUTHD
689     { "saslauthd", &saslauthd_verify_password },
690 #endif
691 #ifdef HAVE_ALWAYSTRUE
692     { "alwaystrue", &always_true },
693 #endif
694     { NULL, NULL }
695 };
696