Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / smtp / smtp_sasl_glue.c
blob2ba880a62ddd9dc49903fd3dcc2f403eba677bed
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* smtp_sasl_glue 3
6 /* SUMMARY
7 /* Postfix SASL interface for SMTP client
8 /* SYNOPSIS
9 /* #include smtp_sasl.h
11 /* void smtp_sasl_initialize()
13 /* void smtp_sasl_connect(session)
14 /* SMTP_SESSION *session;
16 /* void smtp_sasl_start(session, sasl_opts_name, sasl_opts_val)
17 /* SMTP_SESSION *session;
19 /* int smtp_sasl_passwd_lookup(session)
20 /* SMTP_SESSION *session;
22 /* int smtp_sasl_authenticate(session, why)
23 /* SMTP_SESSION *session;
24 /* DSN_BUF *why;
26 /* void smtp_sasl_cleanup(session)
27 /* SMTP_SESSION *session;
29 /* void smtp_sasl_passivate(session, buf)
30 /* SMTP_SESSION *session;
31 /* VSTRING *buf;
33 /* int smtp_sasl_activate(session, buf)
34 /* SMTP_SESSION *session;
35 /* char *buf;
36 /* DESCRIPTION
37 /* smtp_sasl_initialize() initializes the SASL library. This
38 /* routine must be called once at process startup, before any
39 /* chroot operations.
41 /* smtp_sasl_connect() performs per-session initialization. This
42 /* routine must be called once at the start of each connection.
44 /* smtp_sasl_start() performs per-session initialization. This
45 /* routine must be called once per session before doing any SASL
46 /* authentication. The sasl_opts_name and sasl_opts_val parameters are
47 /* the postfix configuration parameters setting the security
48 /* policy of the SASL authentication.
50 /* smtp_sasl_passwd_lookup() looks up the username/password
51 /* for the current SMTP server. The result is zero in case
52 /* of failure.
54 /* smtp_sasl_authenticate() implements the SASL authentication
55 /* dialog. The result is < 0 in case of protocol failure, zero in
56 /* case of unsuccessful authentication, > 0 in case of success.
57 /* The why argument is updated with a reason for failure.
58 /* This routine must be called only when smtp_sasl_passwd_lookup()
59 /* succeeds.
61 /* smtp_sasl_cleanup() cleans up. It must be called at the
62 /* end of every SMTP session that uses SASL authentication.
63 /* This routine is a noop for non-SASL sessions.
65 /* smtp_sasl_passivate() appends flattened SASL attributes to the
66 /* specified buffer. The SASL attributes are not destroyed.
68 /* smtp_sasl_activate() restores SASL attributes from the
69 /* specified buffer. The buffer is modified. A result < 0
70 /* means there was an error.
72 /* Arguments:
73 /* .IP session
74 /* Session context.
75 /* .IP mech_list
76 /* String of SASL mechanisms (separated by blanks)
77 /* DIAGNOSTICS
78 /* All errors are fatal.
79 /* LICENSE
80 /* .ad
81 /* .fi
82 /* The Secure Mailer license must be distributed with this software.
83 /* AUTHOR(S)
84 /* Original author:
85 /* Till Franke
86 /* SuSE Rhein/Main AG
87 /* 65760 Eschborn, Germany
89 /* Adopted by:
90 /* Wietse Venema
91 /* IBM T.J. Watson Research
92 /* P.O. Box 704
93 /* Yorktown Heights, NY 10598, USA
94 /*--*/
97 * System library.
99 #include <sys_defs.h>
100 #include <stdlib.h>
101 #include <string.h>
104 * Utility library
106 #include <msg.h>
107 #include <mymalloc.h>
108 #include <stringops.h>
109 #include <split_at.h>
112 * Global library
114 #include <mail_params.h>
115 #include <string_list.h>
116 #include <maps.h>
117 #include <mail_addr_find.h>
120 * XSASL library.
122 #include <xsasl.h>
125 * Application-specific
127 #include "smtp.h"
128 #include "smtp_sasl.h"
129 #include "smtp_sasl_auth_cache.h"
131 #ifdef USE_SASL_AUTH
134 * Per-host login/password information.
136 static MAPS *smtp_sasl_passwd_map;
139 * Supported SASL mechanisms.
141 STRING_LIST *smtp_sasl_mechs;
144 * SASL implementation handle.
146 static XSASL_CLIENT_IMPL *smtp_sasl_impl;
149 * The 535 SASL authentication failure cache.
151 #ifdef HAVE_SASL_AUTH_CACHE
152 static SMTP_SASL_AUTH_CACHE *smtp_sasl_auth_cache;
154 #endif
156 /* smtp_sasl_passwd_lookup - password lookup routine */
158 int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
160 const char *myname = "smtp_sasl_passwd_lookup";
161 SMTP_STATE *state = session->state;
162 const char *value;
163 char *passwd;
166 * Sanity check.
168 if (smtp_sasl_passwd_map == 0)
169 msg_panic("%s: passwd map not initialized", myname);
172 * Look up the per-server password information. Try the hostname first,
173 * then try the destination.
175 * XXX Instead of using nexthop (the intended destination) we use dest
176 * (either the intended destination, or a fall-back destination).
178 * XXX SASL authentication currently depends on the host/domain but not on
179 * the TCP port. If the port is not :25, we should append it to the table
180 * lookup key. Code for this was briefly introduced into 2.2 snapshots,
181 * but didn't canonicalize the TCP port, and did not append the port to
182 * the MX hostname.
184 if (((state->misc_flags & SMTP_MISC_FLAG_USE_LMTP) == 0
185 && var_smtp_sender_auth && state->request->sender[0]
186 && (value = mail_addr_find(smtp_sasl_passwd_map,
187 state->request->sender, (char **) 0)) != 0)
188 || (value = maps_find(smtp_sasl_passwd_map, session->host, 0)) != 0
189 || (value = maps_find(smtp_sasl_passwd_map, session->dest, 0)) != 0) {
190 if (session->sasl_username)
191 myfree(session->sasl_username);
192 session->sasl_username = mystrdup(value);
193 passwd = split_at(session->sasl_username, ':');
194 if (session->sasl_passwd)
195 myfree(session->sasl_passwd);
196 session->sasl_passwd = mystrdup(passwd ? passwd : "");
197 if (msg_verbose)
198 msg_info("%s: host `%s' user `%s' pass `%s'",
199 myname, session->host,
200 session->sasl_username, session->sasl_passwd);
201 return (1);
202 } else {
203 if (msg_verbose)
204 msg_info("%s: no auth info found (sender=`%s', host=`%s')",
205 myname, state->request->sender, session->host);
206 return (0);
210 /* smtp_sasl_initialize - per-process initialization (pre jail) */
212 void smtp_sasl_initialize(void)
216 * Sanity check.
218 if (smtp_sasl_passwd_map || smtp_sasl_impl)
219 msg_panic("smtp_sasl_initialize: repeated call");
220 if (*var_smtp_sasl_passwd == 0)
221 msg_fatal("specify a password table via the `%s' configuration parameter",
222 VAR_SMTP_SASL_PASSWD);
225 * Open the per-host password table and initialize the SASL library. Use
226 * shared locks for reading, just in case someone updates the table.
228 smtp_sasl_passwd_map = maps_create("smtp_sasl_passwd",
229 var_smtp_sasl_passwd,
230 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
231 if ((smtp_sasl_impl = xsasl_client_init(var_smtp_sasl_type,
232 var_smtp_sasl_path)) == 0)
233 msg_fatal("SASL library initialization");
236 * Initialize optional supported mechanism matchlist
238 if (*var_smtp_sasl_mechs)
239 smtp_sasl_mechs = string_list_init(MATCH_FLAG_NONE,
240 var_smtp_sasl_mechs);
243 * Initialize the 535 SASL authentication failure cache.
245 if (*var_smtp_sasl_auth_cache_name) {
246 #ifdef HAVE_SASL_AUTH_CACHE
247 smtp_sasl_auth_cache =
248 smtp_sasl_auth_cache_init(var_smtp_sasl_auth_cache_name,
249 var_smtp_sasl_auth_cache_time);
250 #else
251 msg_warn("not compiled with TLS support -- "
252 "ignoring the " VAR_SMTP_SASL_AUTH_CACHE_NAME " setting");
253 #endif
257 /* smtp_sasl_connect - per-session client initialization */
259 void smtp_sasl_connect(SMTP_SESSION *session)
263 * This initialization happens whenever we instantiate an SMTP session
264 * object. We don't instantiate a SASL client until we actually need one.
266 session->sasl_mechanism_list = 0;
267 session->sasl_username = 0;
268 session->sasl_passwd = 0;
269 session->sasl_client = 0;
270 session->sasl_reply = 0;
273 /* smtp_sasl_start - per-session SASL initialization */
275 void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
276 const char *sasl_opts_val)
278 XSASL_CLIENT_CREATE_ARGS create_args;
280 if (msg_verbose)
281 msg_info("starting new SASL client");
282 if ((session->sasl_client =
283 XSASL_CLIENT_CREATE(smtp_sasl_impl, &create_args,
284 stream = session->stream,
285 service = var_procname,
286 server_name = session->host,
287 security_options = sasl_opts_val)) == 0)
288 msg_fatal("SASL per-connection initialization failed");
289 session->sasl_reply = vstring_alloc(20);
292 /* smtp_sasl_authenticate - run authentication protocol */
294 int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
296 const char *myname = "smtp_sasl_authenticate";
297 SMTP_RESP *resp;
298 const char *mechanism;
299 int result;
300 char *line;
301 int steps = 0;
304 * Sanity check.
306 if (session->sasl_mechanism_list == 0)
307 msg_panic("%s: no mechanism list", myname);
309 if (msg_verbose)
310 msg_info("%s: %s: SASL mechanisms %s",
311 myname, session->namaddrport, session->sasl_mechanism_list);
314 * Avoid repeated login failures after a recent 535 error.
316 #ifdef HAVE_SASL_AUTH_CACHE
317 if (smtp_sasl_auth_cache
318 && smtp_sasl_auth_cache_find(smtp_sasl_auth_cache, session)) {
319 char *resp_dsn = smtp_sasl_auth_cache_dsn(smtp_sasl_auth_cache);
320 char *resp_str = smtp_sasl_auth_cache_text(smtp_sasl_auth_cache);
322 if (var_smtp_sasl_auth_soft_bounce && resp_dsn[0] == '5')
323 resp_dsn[0] = '4';
324 dsb_update(why, resp_dsn, DSB_DEF_ACTION, DSB_MTYPE_DNS,
325 session->host, var_procname, resp_str,
326 "SASL [CACHED] authentication failed; server %s said: %s",
327 session->host, resp_str);
328 return (0);
330 #endif
333 * Start the client side authentication protocol.
335 result = xsasl_client_first(session->sasl_client,
336 session->sasl_mechanism_list,
337 session->sasl_username,
338 session->sasl_passwd,
339 &mechanism, session->sasl_reply);
340 if (result != XSASL_AUTH_OK) {
341 dsb_update(why, "4.7.0", DSB_DEF_ACTION, DSB_SKIP_RMTA,
342 DSB_DTYPE_SASL, STR(session->sasl_reply),
343 "SASL authentication failed; "
344 "cannot authenticate to server %s: %s",
345 session->namaddr, STR(session->sasl_reply));
346 return (-1);
350 * Send the AUTH command and the optional initial client response.
351 * sasl_encode64() produces four bytes for each complete or incomplete
352 * triple of input bytes. Allocate an extra byte for string termination.
354 if (LEN(session->sasl_reply) > 0) {
355 smtp_chat_cmd(session, "AUTH %s %s", mechanism,
356 STR(session->sasl_reply));
357 } else {
358 smtp_chat_cmd(session, "AUTH %s", mechanism);
362 * Step through the authentication protocol until the server tells us
363 * that we are done.
365 while ((resp = smtp_chat_resp(session))->code / 100 == 3) {
368 * Sanity check.
370 if (++steps > 100) {
371 dsb_simple(why, "4.3.0", "SASL authentication failed; "
372 "authentication protocol loop with server %s",
373 session->namaddr);
374 return (-1);
378 * Process a server challenge.
380 line = resp->str;
381 (void) mystrtok(&line, "- \t\n"); /* skip over result code */
382 result = xsasl_client_next(session->sasl_client, line,
383 session->sasl_reply);
384 if (result != XSASL_AUTH_OK) {
385 dsb_update(why, "4.7.0", DSB_DEF_ACTION, /* Fix 200512 */
386 DSB_SKIP_RMTA, DSB_DTYPE_SASL, STR(session->sasl_reply),
387 "SASL authentication failed; "
388 "cannot authenticate to server %s: %s",
389 session->namaddr, STR(session->sasl_reply));
390 return (-1); /* Fix 200512 */
394 * Send a client response.
396 smtp_chat_cmd(session, "%s", STR(session->sasl_reply));
400 * We completed the authentication protocol.
402 if (resp->code / 100 != 2) {
403 #ifdef HAVE_SASL_AUTH_CACHE
404 /* Update the 535 authentication failure cache. */
405 if (smtp_sasl_auth_cache && resp->code == 535)
406 smtp_sasl_auth_cache_store(smtp_sasl_auth_cache, session, resp);
407 #endif
408 if (var_smtp_sasl_auth_soft_bounce && resp->code / 100 == 5)
409 STR(resp->dsn_buf)[0] = '4';
410 dsb_update(why, resp->dsn, DSB_DEF_ACTION,
411 DSB_MTYPE_DNS, session->host,
412 var_procname, resp->str,
413 "SASL authentication failed; server %s said: %s",
414 session->namaddr, resp->str);
415 return (0);
417 return (1);
420 /* smtp_sasl_cleanup - per-session cleanup */
422 void smtp_sasl_cleanup(SMTP_SESSION *session)
424 if (session->sasl_username) {
425 myfree(session->sasl_username);
426 session->sasl_username = 0;
428 if (session->sasl_passwd) {
429 myfree(session->sasl_passwd);
430 session->sasl_passwd = 0;
432 if (session->sasl_mechanism_list) {
433 /* allocated in smtp_sasl_helo_auth */
434 myfree(session->sasl_mechanism_list);
435 session->sasl_mechanism_list = 0;
437 if (session->sasl_client) {
438 if (msg_verbose)
439 msg_info("disposing SASL state information");
440 xsasl_client_free(session->sasl_client);
441 session->sasl_client = 0;
443 if (session->sasl_reply) {
444 vstring_free(session->sasl_reply);
445 session->sasl_reply = 0;
449 /* smtp_sasl_passivate - append serialized SASL attributes */
451 void smtp_sasl_passivate(SMTP_SESSION *session, VSTRING *buf)
455 /* smtp_sasl_activate - de-serialize SASL attributes */
457 int smtp_sasl_activate(SMTP_SESSION *session, char *buf)
459 return (0);
462 #endif