No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / xsasl / xsasl_cyrus_client.c
blobba5b041891d104f7afb31de40dd9552602a902cc
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* xsasl_cyrus_client 3
6 /* SUMMARY
7 /* Cyrus SASL client-side plug-in
8 /* SYNOPSIS
9 /* #include <xsasl_cyrus_client.h>
11 /* XSASL_CLIENT_IMPL *xsasl_cyrus_client_init(client_type, path_info)
12 /* const char *client_type;
13 /* DESCRIPTION
14 /* This module implements the Cyrus SASL client-side authentication
15 /* plug-in.
17 /* xsasl_cyrus_client_init() initializes the Cyrus SASL library and
18 /* returns an implementation handle that can be used to generate
19 /* SASL client instances.
21 /* Arguments:
22 /* .IP client_type
23 /* The plug-in SASL client type (cyrus). This argument is
24 /* ignored, but it could be used when one implementation
25 /* provides multiple variants.
26 /* .IP path_info
27 /* Implementation-specific information to specify the location
28 /* of a configuration file, rendez-vous point, etc. This
29 /* information is ignored by the Cyrus SASL client plug-in.
30 /* DIAGNOSTICS
31 /* Fatal: out of memory.
33 /* Panic: interface violation.
35 /* Other: the routines log a warning and return an error result
36 /* as specified in xsasl_client(3).
37 /* SEE ALSO
38 /* xsasl_client(3) Client API
39 /* LICENSE
40 /* .ad
41 /* .fi
42 /* The Secure Mailer license must be distributed with this software.
43 /* AUTHOR(S)
44 /* Original author:
45 /* Till Franke
46 /* SuSE Rhein/Main AG
47 /* 65760 Eschborn, Germany
49 /* Adopted by:
50 /* Wietse Venema
51 /* IBM T.J. Watson Research
52 /* P.O. Box 704
53 /* Yorktown Heights, NY 10598, USA
54 /*--*/
57 * System library.
59 #include <sys_defs.h>
60 #include <stdlib.h>
61 #include <string.h>
64 * Utility library
66 #include <msg.h>
67 #include <mymalloc.h>
68 #include <stringops.h>
71 * Global library
73 #include <mail_params.h>
76 * Application-specific
78 #include <xsasl.h>
79 #include <xsasl_cyrus.h>
80 #include <xsasl_cyrus_common.h>
82 #if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
84 #include <sasl.h>
85 #include <saslutil.h>
88 * Silly little macros.
90 #define STR(s) vstring_str(s)
93 * Macros to handle API differences between SASLv1 and SASLv2. Specifics:
95 * The SASL_LOG_* constants were renamed in SASLv2.
97 * SASLv2's sasl_client_new takes two new parameters to specify local and
98 * remote IP addresses for auth mechs that use them.
100 * SASLv2's sasl_client_start function no longer takes the secret parameter.
102 * SASLv2's sasl_decode64 function takes an extra parameter for the length of
103 * the output buffer.
105 * The other major change is that SASLv2 now takes more responsibility for
106 * deallocating memory that it allocates internally. Thus, some of the
107 * function parameters are now 'const', to make sure we don't try to free
108 * them too. This is dealt with in the code later on.
110 #if SASL_VERSION_MAJOR < 2
111 /* SASL version 1.x */
112 #define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
113 sasl_client_new(srv, fqdn, prompt, secflags, pconn)
114 #define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
115 sasl_client_start(conn, mechlst, secret, prompt, clout, cllen, mech)
116 #define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
117 sasl_decode64(in, inlen, out, outlen)
118 typedef char *CLIENTOUT_TYPE;
120 #endif
122 #if SASL_VERSION_MAJOR >= 2
123 /* SASL version > 2.x */
124 #define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
125 sasl_client_new(srv, fqdn, lport, rport, prompt, secflags, pconn)
126 #define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
127 sasl_client_start(conn, mechlst, prompt, clout, cllen, mech)
128 #define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
129 sasl_decode64(in, inlen, out, outmaxlen, outlen)
130 typedef const char *CLIENTOUT_TYPE;
132 #endif
135 * The XSASL_CYRUS_CLIENT object is derived from the generic XSASL_CLIENT
136 * object.
138 typedef struct {
139 XSASL_CLIENT xsasl; /* generic members, must be first */
140 VSTREAM *stream; /* client-server connection */
141 sasl_conn_t *sasl_conn; /* SASL context */
142 VSTRING *decoded; /* decoded server challenge */
143 sasl_callback_t *callbacks; /* user/password lookup */
144 char *username;
145 char *password;
146 } XSASL_CYRUS_CLIENT;
149 * Forward declarations.
151 static void xsasl_cyrus_client_done(XSASL_CLIENT_IMPL *);
152 static XSASL_CLIENT *xsasl_cyrus_client_create(XSASL_CLIENT_IMPL *,
153 XSASL_CLIENT_CREATE_ARGS *);
154 static int xsasl_cyrus_client_set_security(XSASL_CLIENT *, const char *);
155 static int xsasl_cyrus_client_first(XSASL_CLIENT *, const char *, const char *,
156 const char *, const char **, VSTRING *);
157 static int xsasl_cyrus_client_next(XSASL_CLIENT *, const char *, VSTRING *);
158 static void xsasl_cyrus_client_free(XSASL_CLIENT *);
160 /* xsasl_cyrus_client_get_user - username lookup call-back routine */
162 static int xsasl_cyrus_client_get_user(void *context, int unused_id,
163 const char **result,
164 unsigned *len)
166 const char *myname = "xsasl_cyrus_client_get_user";
167 XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
169 if (msg_verbose)
170 msg_info("%s: %s", myname, client->username);
173 * Sanity check.
175 if (client->password == 0)
176 msg_panic("%s: no username looked up", myname);
178 *result = client->username;
179 if (len)
180 *len = strlen(client->username);
181 return (SASL_OK);
184 /* xsasl_cyrus_client_get_passwd - password lookup call-back routine */
186 static int xsasl_cyrus_client_get_passwd(sasl_conn_t *conn, void *context,
187 int id, sasl_secret_t **psecret)
189 const char *myname = "xsasl_cyrus_client_get_passwd";
190 XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
191 int len;
193 if (msg_verbose)
194 msg_info("%s: %s", myname, client->password);
197 * Sanity check.
199 if (!conn || !psecret || id != SASL_CB_PASS)
200 return (SASL_BADPARAM);
201 if (client->password == 0)
202 msg_panic("%s: no password looked up", myname);
205 * Convert the password into a counted string.
207 len = strlen(client->password);
208 if ((*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len)) == 0)
209 return (SASL_NOMEM);
210 (*psecret)->len = len;
211 memcpy((*psecret)->data, client->password, len + 1);
213 return (SASL_OK);
216 /* xsasl_cyrus_client_init - initialize Cyrus SASL library */
218 XSASL_CLIENT_IMPL *xsasl_cyrus_client_init(const char *unused_client_type,
219 const char *unused_path_info)
221 XSASL_CLIENT_IMPL *xp;
222 int sasl_status;
225 * Global callbacks. These have no per-session context.
227 static sasl_callback_t callbacks[] = {
228 {SASL_CB_LOG, &xsasl_cyrus_log, 0},
229 {SASL_CB_LIST_END, 0, 0}
232 #if SASL_VERSION_MAJOR >= 2 && (SASL_VERSION_MINOR >= 2 \
233 || (SASL_VERSION_MINOR == 1 && SASL_VERSION_STEP >= 19))
234 int sasl_major;
235 int sasl_minor;
236 int sasl_step;
239 * DLL hell guard.
241 sasl_version_info((const char **) 0, (const char **) 0,
242 &sasl_major, &sasl_minor,
243 &sasl_step, (int *) 0);
244 if (sasl_major != SASL_VERSION_MAJOR
245 #if 0
246 || sasl_minor != SASL_VERSION_MINOR
247 || sasl_step != SASL_VERSION_STEP
248 #endif
250 msg_warn("incorrect SASL library version. "
251 "Postfix was built with include files from version %d.%d.%d, "
252 "but the run-time library version is %d.%d.%d",
253 SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
254 sasl_major, sasl_minor, sasl_step);
255 return (0);
257 #endif
259 if (*var_cyrus_conf_path) {
260 #ifdef SASL_PATH_TYPE_CONFIG /* Cyrus SASL 2.1.22 */
261 if (sasl_set_path(SASL_PATH_TYPE_CONFIG,
262 var_cyrus_conf_path) != SASL_OK)
263 msg_warn("failed to set Cyrus SASL configuration path: \"%s\"",
264 var_cyrus_conf_path);
265 #else
266 msg_warn("%s is not empty, but setting the Cyrus SASL configuration "
267 "path is not supported with SASL library version %d.%d.%d",
268 VAR_CYRUS_CONF_PATH, SASL_VERSION_MAJOR,
269 SASL_VERSION_MINOR, SASL_VERSION_STEP);
270 #endif
274 * Initialize the SASL library.
276 if ((sasl_status = sasl_client_init(callbacks)) != SASL_OK) {
277 msg_warn("SASL library initialization error: %s",
278 xsasl_cyrus_strerror(sasl_status));
279 return (0);
283 * Return a generic XSASL_CLIENT_IMPL object. We don't need to extend it
284 * with our own methods or data.
286 xp = (XSASL_CLIENT_IMPL *) mymalloc(sizeof(*xp));
287 xp->create = xsasl_cyrus_client_create;
288 xp->done = xsasl_cyrus_client_done;
289 return (xp);
292 /* xsasl_cyrus_client_done - dispose of implementation */
294 static void xsasl_cyrus_client_done(XSASL_CLIENT_IMPL *impl)
296 myfree((char *) impl);
297 sasl_done();
300 /* xsasl_cyrus_client_create - per-session SASL initialization */
302 XSASL_CLIENT *xsasl_cyrus_client_create(XSASL_CLIENT_IMPL *unused_impl,
303 XSASL_CLIENT_CREATE_ARGS *args)
305 XSASL_CYRUS_CLIENT *client = 0;
306 static sasl_callback_t callbacks[] = {
307 {SASL_CB_USER, &xsasl_cyrus_client_get_user, 0},
308 {SASL_CB_AUTHNAME, &xsasl_cyrus_client_get_user, 0},
309 {SASL_CB_PASS, &xsasl_cyrus_client_get_passwd, 0},
310 {SASL_CB_LIST_END, 0, 0}
312 sasl_conn_t *sasl_conn = 0;
313 sasl_callback_t *custom_callbacks = 0;
314 sasl_callback_t *cp;
315 int sasl_status;
318 * The optimizer will eliminate code duplication and/or dead code.
320 #define XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(x) \
321 do { \
322 if (client) { \
323 xsasl_cyrus_client_free(&client->xsasl); \
324 } else { \
325 if (custom_callbacks) \
326 myfree((char *) custom_callbacks); \
327 if (sasl_conn) \
328 sasl_dispose(&sasl_conn); \
330 return (x); \
331 } while (0)
334 * Per-session initialization. Provide each session with its own callback
335 * context.
337 #define NULL_SECFLAGS 0
339 custom_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks));
340 memcpy((char *) custom_callbacks, callbacks, sizeof(callbacks));
342 #define NULL_SERVER_ADDR ((char *) 0)
343 #define NULL_CLIENT_ADDR ((char *) 0)
345 if ((sasl_status = SASL_CLIENT_NEW(args->service, args->server_name,
346 NULL_CLIENT_ADDR, NULL_SERVER_ADDR,
347 var_cyrus_sasl_authzid ? custom_callbacks :
348 custom_callbacks + 1, NULL_SECFLAGS,
349 &sasl_conn)) != SASL_OK) {
350 msg_warn("per-session SASL client initialization: %s",
351 xsasl_cyrus_strerror(sasl_status));
352 XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(0);
356 * Extend the XSASL_CLIENT object with our own state. We use long-lived
357 * conversion buffers rather than local variables to avoid memory leaks
358 * in case of read/write timeout or I/O error.
360 * XXX If we enable SASL encryption, there needs to be a way to inform the
361 * application, so that they can turn off connection caching, refuse
362 * STARTTLS, etc.
364 client = (XSASL_CYRUS_CLIENT *) mymalloc(sizeof(*client));
365 client->xsasl.free = xsasl_cyrus_client_free;
366 client->xsasl.first = xsasl_cyrus_client_first;
367 client->xsasl.next = xsasl_cyrus_client_next;
368 client->stream = args->stream;
369 client->sasl_conn = sasl_conn;
370 client->callbacks = custom_callbacks;
371 client->decoded = vstring_alloc(20);
372 client->username = 0;
373 client->password = 0;
375 for (cp = custom_callbacks; cp->id != SASL_CB_LIST_END; cp++)
376 cp->context = (void *) client;
378 if (xsasl_cyrus_client_set_security(&client->xsasl,
379 args->security_options)
380 != XSASL_AUTH_OK)
381 XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(0);
383 return (&client->xsasl);
386 /* xsasl_cyrus_client_set_security - set security properties */
388 static int xsasl_cyrus_client_set_security(XSASL_CLIENT *xp,
389 const char *sasl_opts_val)
391 XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
392 sasl_security_properties_t sec_props;
393 int sasl_status;
396 * Per-session security properties. XXX This routine is not sufficiently
397 * documented. What is the purpose of all this?
399 memset(&sec_props, 0, sizeof(sec_props));
400 sec_props.min_ssf = 0;
401 sec_props.max_ssf = 0; /* don't allow real SASL
402 * security layer */
403 if (*sasl_opts_val == 0) {
404 sec_props.security_flags = 0;
405 } else {
406 sec_props.security_flags =
407 xsasl_cyrus_security_parse_opts(sasl_opts_val);
408 if (sec_props.security_flags == 0) {
409 msg_warn("bad per-session SASL security properties");
410 return (XSASL_AUTH_FAIL);
413 sec_props.maxbufsize = 0;
414 sec_props.property_names = 0;
415 sec_props.property_values = 0;
416 if ((sasl_status = sasl_setprop(client->sasl_conn, SASL_SEC_PROPS,
417 &sec_props)) != SASL_OK) {
418 msg_warn("set per-session SASL security properties: %s",
419 xsasl_cyrus_strerror(sasl_status));
420 return (XSASL_AUTH_FAIL);
422 return (XSASL_AUTH_OK);
425 /* xsasl_cyrus_client_first - run authentication protocol */
427 static int xsasl_cyrus_client_first(XSASL_CLIENT *xp,
428 const char *mechanism_list,
429 const char *username,
430 const char *password,
431 const char **mechanism,
432 VSTRING *init_resp)
434 const char *myname = "xsasl_cyrus_client_first";
435 XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
436 unsigned enc_length;
437 unsigned enc_length_out;
438 CLIENTOUT_TYPE clientout;
439 unsigned clientoutlen;
440 int sasl_status;
442 #define NO_SASL_SECRET 0
443 #define NO_SASL_INTERACTION 0
446 * Save the username and password for the call-backs.
448 if (client->username)
449 myfree(client->username);
450 client->username = mystrdup(username);
451 if (client->password)
452 myfree(client->password);
453 client->password = mystrdup(password);
456 * Start the client side authentication protocol.
458 sasl_status = SASL_CLIENT_START((sasl_conn_t *) client->sasl_conn,
459 mechanism_list,
460 NO_SASL_SECRET, NO_SASL_INTERACTION,
461 &clientout, &clientoutlen, mechanism);
462 if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
463 vstring_strcpy(init_resp, xsasl_cyrus_strerror(sasl_status));
464 return (XSASL_AUTH_FAIL);
468 * Generate the AUTH command and the optional initial client response.
469 * sasl_encode64() produces four bytes for each complete or incomplete
470 * triple of input bytes. Allocate an extra byte for string termination.
472 #define ENCODE64_LENGTH(n) ((((n) + 2) / 3) * 4)
474 if (clientoutlen > 0) {
475 if (msg_verbose) {
476 escape(client->decoded, clientout, clientoutlen);
477 msg_info("%s: uncoded initial reply: %s",
478 myname, STR(client->decoded));
480 enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
481 VSTRING_RESET(init_resp); /* Fix 200512 */
482 VSTRING_SPACE(init_resp, enc_length);
483 if ((sasl_status = sasl_encode64(clientout, clientoutlen,
484 STR(init_resp),
485 vstring_avail(init_resp),
486 &enc_length_out)) != SASL_OK)
487 msg_panic("%s: sasl_encode64 botch: %s",
488 myname, xsasl_cyrus_strerror(sasl_status));
489 VSTRING_AT_OFFSET(init_resp, enc_length_out); /* XXX */
490 #if SASL_VERSION_MAJOR < 2
491 /* SASL version 1 doesn't free memory that it allocates. */
492 free(clientout);
493 #endif
494 } else {
495 vstring_strcpy(init_resp, "");
497 return (XSASL_AUTH_OK);
500 /* xsasl_cyrus_client_next - continue authentication */
502 static int xsasl_cyrus_client_next(XSASL_CLIENT *xp, const char *server_reply,
503 VSTRING *client_reply)
505 const char *myname = "xsasl_cyrus_client_next";
506 XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
507 unsigned enc_length;
508 unsigned enc_length_out;
509 CLIENTOUT_TYPE clientout;
510 unsigned clientoutlen;
511 unsigned serverinlen;
512 int sasl_status;
515 * Process a server challenge.
517 serverinlen = strlen(server_reply);
518 VSTRING_RESET(client->decoded); /* Fix 200512 */
519 VSTRING_SPACE(client->decoded, serverinlen);
520 if ((sasl_status = SASL_DECODE64(server_reply, serverinlen,
521 STR(client->decoded),
522 vstring_avail(client->decoded),
523 &enc_length)) != SASL_OK) {
524 vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
525 return (XSASL_AUTH_FORM);
527 if (msg_verbose)
528 msg_info("%s: decoded challenge: %.*s",
529 myname, (int) enc_length, STR(client->decoded));
530 sasl_status = sasl_client_step(client->sasl_conn, STR(client->decoded),
531 enc_length, NO_SASL_INTERACTION,
532 &clientout, &clientoutlen);
533 if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
534 vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
535 return (XSASL_AUTH_FAIL);
539 * Send a client response.
541 if (clientoutlen > 0) {
542 if (msg_verbose)
543 msg_info("%s: uncoded client response %.*s",
544 myname, (int) clientoutlen, clientout);
545 enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
546 VSTRING_RESET(client_reply); /* Fix 200512 */
547 VSTRING_SPACE(client_reply, enc_length);
548 if ((sasl_status = sasl_encode64(clientout, clientoutlen,
549 STR(client_reply),
550 vstring_avail(client_reply),
551 &enc_length_out)) != SASL_OK)
552 msg_panic("%s: sasl_encode64 botch: %s",
553 myname, xsasl_cyrus_strerror(sasl_status));
554 #if SASL_VERSION_MAJOR < 2
555 /* SASL version 1 doesn't free memory that it allocates. */
556 free(clientout);
557 #endif
558 } else {
559 /* XXX Can't happen. */
560 vstring_strcpy(client_reply, "");
562 return (XSASL_AUTH_OK);
565 /* xsasl_cyrus_client_free - per-session cleanup */
567 void xsasl_cyrus_client_free(XSASL_CLIENT *xp)
569 XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
571 if (client->username)
572 myfree(client->username);
573 if (client->password)
574 myfree(client->password);
575 if (client->sasl_conn)
576 sasl_dispose(&client->sasl_conn);
577 myfree((char *) client->callbacks);
578 vstring_free(client->decoded);
579 myfree((char *) client);
582 #endif