Postfix3.3.1
smtp_sasl_glue.c
[詳解]
1 /*++
2 /* NAME
3 /* smtp_sasl_glue 3
4 /* SUMMARY
5 /* Postfix SASL interface for SMTP client
6 /* SYNOPSIS
7 /* #include smtp_sasl.h
8 /*
9 /* void smtp_sasl_initialize()
10 /*
11 /* void smtp_sasl_connect(session)
12 /* SMTP_SESSION *session;
13 /*
14 /* void smtp_sasl_start(session, sasl_opts_name, sasl_opts_val)
15 /* SMTP_SESSION *session;
16 /*
17 /* int smtp_sasl_passwd_lookup(session)
18 /* SMTP_SESSION *session;
19 /*
20 /* int smtp_sasl_authenticate(session, why)
21 /* SMTP_SESSION *session;
22 /* DSN_BUF *why;
23 /*
24 /* void smtp_sasl_cleanup(session)
25 /* SMTP_SESSION *session;
26 /*
27 /* void smtp_sasl_passivate(session, buf)
28 /* SMTP_SESSION *session;
29 /* VSTRING *buf;
30 /*
31 /* int smtp_sasl_activate(session, buf)
32 /* SMTP_SESSION *session;
33 /* char *buf;
34 /* DESCRIPTION
35 /* smtp_sasl_initialize() initializes the SASL library. This
36 /* routine must be called once at process startup, before any
37 /* chroot operations.
38 /*
39 /* smtp_sasl_connect() performs per-session initialization. This
40 /* routine must be called once at the start of each connection.
41 /*
42 /* smtp_sasl_start() performs per-session initialization. This
43 /* routine must be called once per session before doing any SASL
44 /* authentication. The sasl_opts_name and sasl_opts_val parameters are
45 /* the postfix configuration parameters setting the security
46 /* policy of the SASL authentication.
47 /*
48 /* smtp_sasl_passwd_lookup() looks up the username/password
49 /* for the current SMTP server. The result is zero in case
50 /* of failure, a long jump in case of error.
51 /*
52 /* smtp_sasl_authenticate() implements the SASL authentication
53 /* dialog. The result is < 0 in case of protocol failure, zero in
54 /* case of unsuccessful authentication, > 0 in case of success.
55 /* The why argument is updated with a reason for failure.
56 /* This routine must be called only when smtp_sasl_passwd_lookup()
57 /* succeeds.
58 /*
59 /* smtp_sasl_cleanup() cleans up. It must be called at the
60 /* end of every SMTP session that uses SASL authentication.
61 /* This routine is a noop for non-SASL sessions.
62 /*
63 /* smtp_sasl_passivate() appends flattened SASL attributes to the
64 /* specified buffer. The SASL attributes are not destroyed.
65 /*
66 /* smtp_sasl_activate() restores SASL attributes from the
67 /* specified buffer. The buffer is modified. A result < 0
68 /* means there was an error.
69 /*
70 /* Arguments:
71 /* .IP session
72 /* Session context.
73 /* .IP mech_list
74 /* String of SASL mechanisms (separated by blanks)
75 /* DIAGNOSTICS
76 /* All errors are fatal.
77 /* LICENSE
78 /* .ad
79 /* .fi
80 /* The Secure Mailer license must be distributed with this software.
81 /* AUTHOR(S)
82 /* Original author:
83 /* Till Franke
84 /* SuSE Rhein/Main AG
85 /* 65760 Eschborn, Germany
86 /*
87 /* Adopted by:
88 /* Wietse Venema
89 /* IBM T.J. Watson Research
90 /* P.O. Box 704
91 /* Yorktown Heights, NY 10598, USA
92 /*--*/
93 
94  /*
95  * System library.
96  */
97 #include <sys_defs.h>
98 #include <stdlib.h>
99 #include <string.h>
100 
101  /*
102  * Utility library
103  */
104 #include <msg.h>
105 #include <mymalloc.h>
106 #include <stringops.h>
107 #include <split_at.h>
108 
109  /*
110  * Global library
111  */
112 #include <mail_params.h>
113 #include <string_list.h>
114 #include <maps.h>
115 #include <mail_addr_find.h>
116 #include <smtp_stream.h>
117 
118  /*
119  * XSASL library.
120  */
121 #include <xsasl.h>
122 
123  /*
124  * Application-specific
125  */
126 #include "smtp.h"
127 #include "smtp_sasl.h"
128 #include "smtp_sasl_auth_cache.h"
129 
130 #ifdef USE_SASL_AUTH
131 
132  /*
133  * Per-host login/password information.
134  */
135 static MAPS *smtp_sasl_passwd_map;
136 
137  /*
138  * Supported SASL mechanisms.
139  */
141 
142  /*
143  * SASL implementation handle.
144  */
145 static XSASL_CLIENT_IMPL *smtp_sasl_impl;
146 
147  /*
148  * The 535 SASL authentication failure cache.
149  */
150 #ifdef HAVE_SASL_AUTH_CACHE
151 static SMTP_SASL_AUTH_CACHE *smtp_sasl_auth_cache;
152 
153 #endif
154 
155 /* smtp_sasl_passwd_lookup - password lookup routine */
156 
158 {
159  const char *myname = "smtp_sasl_passwd_lookup";
160  SMTP_STATE *state = session->state;
161  SMTP_ITERATOR *iter = session->iterator;
162  const char *value;
163  char *passwd;
164 
165  /*
166  * Sanity check.
167  */
168  if (smtp_sasl_passwd_map == 0)
169  msg_panic("%s: passwd map not initialized", myname);
170 
171  /*
172  * Look up the per-server password information. Try the hostname first,
173  * then try the destination.
174  *
175  * XXX Instead of using nexthop (the intended destination) we use dest
176  * (either the intended destination, or a fall-back destination).
177  *
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.
183  */
184  smtp_sasl_passwd_map->error = 0;
185  if ((smtp_mode
186  && var_smtp_sender_auth && state->request->sender[0]
187  && (value = mail_addr_find(smtp_sasl_passwd_map,
188  state->request->sender, (char **) 0)) != 0)
189  || (smtp_sasl_passwd_map->error == 0
190  && (value = maps_find(smtp_sasl_passwd_map,
191  STR(iter->host), 0)) != 0)
192  || (smtp_sasl_passwd_map->error == 0
193  && (value = maps_find(smtp_sasl_passwd_map,
194  STR(iter->dest), 0)) != 0)) {
195  if (session->sasl_username)
196  myfree(session->sasl_username);
197  session->sasl_username = mystrdup(value);
198  passwd = split_at(session->sasl_username, ':');
199  if (session->sasl_passwd)
200  myfree(session->sasl_passwd);
201  session->sasl_passwd = mystrdup(passwd ? passwd : "");
202  if (msg_verbose)
203  msg_info("%s: host `%s' user `%s' pass `%s'",
204  myname, STR(iter->host),
205  session->sasl_username, session->sasl_passwd);
206  return (1);
207  } else if (smtp_sasl_passwd_map->error) {
208  msg_warn("%s: %s lookup error",
209  state->request->queue_id, smtp_sasl_passwd_map->title);
211  } else {
212  if (msg_verbose)
213  msg_info("%s: no auth info found (sender=`%s', host=`%s')",
214  myname, state->request->sender, STR(iter->host));
215  return (0);
216  }
217 }
218 
219 /* smtp_sasl_initialize - per-process initialization (pre jail) */
220 
221 void smtp_sasl_initialize(void)
222 {
223 
224  /*
225  * Sanity check.
226  */
227  if (smtp_sasl_passwd_map || smtp_sasl_impl)
228  msg_panic("smtp_sasl_initialize: repeated call");
229  if (*var_smtp_sasl_passwd == 0)
230  msg_fatal("specify a password table via the `%s' configuration parameter",
231  VAR_LMTP_SMTP(SASL_PASSWD));
232 
233  /*
234  * Open the per-host password table and initialize the SASL library. Use
235  * shared locks for reading, just in case someone updates the table.
236  */
237  smtp_sasl_passwd_map = maps_create(VAR_LMTP_SMTP(SASL_PASSWD),
241  if ((smtp_sasl_impl = xsasl_client_init(var_smtp_sasl_type,
242  var_smtp_sasl_path)) == 0)
243  msg_fatal("SASL library initialization");
244 
245  /*
246  * Initialize optional supported mechanism matchlist
247  */
248  if (*var_smtp_sasl_mechs)
252 
253  /*
254  * Initialize the 535 SASL authentication failure cache.
255  */
257 #ifdef HAVE_SASL_AUTH_CACHE
258  smtp_sasl_auth_cache =
259  smtp_sasl_auth_cache_init(var_smtp_sasl_auth_cache_name,
261 #else
262  msg_warn("not compiled with TLS support -- "
263  "ignoring the %s setting", VAR_LMTP_SMTP(SASL_AUTH_CACHE_NAME));
264 #endif
265  }
266 }
267 
268 /* smtp_sasl_connect - per-session client initialization */
269 
270 void smtp_sasl_connect(SMTP_SESSION *session)
271 {
272 
273  /*
274  * This initialization happens whenever we instantiate an SMTP session
275  * object. We don't instantiate a SASL client until we actually need one.
276  */
277  session->sasl_mechanism_list = 0;
278  session->sasl_username = 0;
279  session->sasl_passwd = 0;
280  session->sasl_client = 0;
281  session->sasl_reply = 0;
282 }
283 
284 /* smtp_sasl_start - per-session SASL initialization */
285 
286 void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
287  const char *sasl_opts_val)
288 {
289  XSASL_CLIENT_CREATE_ARGS create_args;
290  SMTP_ITERATOR *iter = session->iterator;
291 
292  if (msg_verbose)
293  msg_info("starting new SASL client");
294  if ((session->sasl_client =
295  XSASL_CLIENT_CREATE(smtp_sasl_impl, &create_args,
296  stream = session->stream,
297  service = var_procname,
298  server_name = STR(iter->host),
299  security_options = sasl_opts_val)) == 0)
300  msg_fatal("SASL per-connection initialization failed");
301  session->sasl_reply = vstring_alloc(20);
302 }
303 
304 /* smtp_sasl_authenticate - run authentication protocol */
305 
306 int smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
307 {
308  const char *myname = "smtp_sasl_authenticate";
309  SMTP_ITERATOR *iter = session->iterator;
310  SMTP_RESP *resp;
311  const char *mechanism;
312  int result;
313  char *line;
314  int steps = 0;
315 
316  /*
317  * Sanity check.
318  */
319  if (session->sasl_mechanism_list == 0)
320  msg_panic("%s: no mechanism list", myname);
321 
322  if (msg_verbose)
323  msg_info("%s: %s: SASL mechanisms %s",
324  myname, session->namaddrport, session->sasl_mechanism_list);
325 
326  /*
327  * Avoid repeated login failures after a recent 535 error.
328  */
329 #ifdef HAVE_SASL_AUTH_CACHE
330  if (smtp_sasl_auth_cache
331  && smtp_sasl_auth_cache_find(smtp_sasl_auth_cache, session)) {
332  char *resp_dsn = smtp_sasl_auth_cache_dsn(smtp_sasl_auth_cache);
333  char *resp_str = smtp_sasl_auth_cache_text(smtp_sasl_auth_cache);
334 
335  if (var_smtp_sasl_auth_soft_bounce && resp_dsn[0] == '5')
336  resp_dsn[0] = '4';
337  dsb_update(why, resp_dsn, DSB_DEF_ACTION, DSB_MTYPE_DNS,
338  STR(iter->host), var_procname, resp_str,
339  "SASL [CACHED] authentication failed; server %s said: %s",
340  STR(iter->host), resp_str);
341  return (0);
342  }
343 #endif
344 
345  /*
346  * Start the client side authentication protocol.
347  */
348  result = xsasl_client_first(session->sasl_client,
349  session->sasl_mechanism_list,
350  session->sasl_username,
351  session->sasl_passwd,
352  &mechanism, session->sasl_reply);
353  if (result != XSASL_AUTH_OK) {
354  dsb_update(why, "4.7.0", DSB_DEF_ACTION, DSB_SKIP_RMTA,
355  DSB_DTYPE_SASL, STR(session->sasl_reply),
356  "SASL authentication failed; "
357  "cannot authenticate to server %s: %s",
358  session->namaddr, STR(session->sasl_reply));
359  return (-1);
360  }
361 
362  /*
363  * Send the AUTH command and the optional initial client response.
364  * sasl_encode64() produces four bytes for each complete or incomplete
365  * triple of input bytes. Allocate an extra byte for string termination.
366  */
367  if (LEN(session->sasl_reply) > 0) {
368  smtp_chat_cmd(session, "AUTH %s %s", mechanism,
369  STR(session->sasl_reply));
370  } else {
371  smtp_chat_cmd(session, "AUTH %s", mechanism);
372  }
373 
374  /*
375  * Step through the authentication protocol until the server tells us
376  * that we are done.
377  */
378  while ((resp = smtp_chat_resp(session))->code / 100 == 3) {
379 
380  /*
381  * Sanity check.
382  */
383  if (++steps > 100) {
384  dsb_simple(why, "4.3.0", "SASL authentication failed; "
385  "authentication protocol loop with server %s",
386  session->namaddr);
387  return (-1);
388  }
389 
390  /*
391  * Process a server challenge.
392  */
393  line = resp->str;
394  (void) mystrtok(&line, "- \t\n"); /* skip over result code */
395  result = xsasl_client_next(session->sasl_client, line,
396  session->sasl_reply);
397  if (result != XSASL_AUTH_OK) {
398  dsb_update(why, "4.7.0", DSB_DEF_ACTION, /* Fix 200512 */
399  DSB_SKIP_RMTA, DSB_DTYPE_SASL, STR(session->sasl_reply),
400  "SASL authentication failed; "
401  "cannot authenticate to server %s: %s",
402  session->namaddr, STR(session->sasl_reply));
403  return (-1); /* Fix 200512 */
404  }
405 
406  /*
407  * Send a client response.
408  */
409  smtp_chat_cmd(session, "%s", STR(session->sasl_reply));
410  }
411 
412  /*
413  * We completed the authentication protocol.
414  */
415  if (resp->code / 100 != 2) {
416 #ifdef HAVE_SASL_AUTH_CACHE
417  /* Update the 535 authentication failure cache. */
418  if (smtp_sasl_auth_cache && resp->code == 535)
419  smtp_sasl_auth_cache_store(smtp_sasl_auth_cache, session, resp);
420 #endif
421  if (var_smtp_sasl_auth_soft_bounce && resp->code / 100 == 5)
422  STR(resp->dsn_buf)[0] = '4';
423  dsb_update(why, resp->dsn, DSB_DEF_ACTION,
424  DSB_MTYPE_DNS, STR(iter->host),
425  var_procname, resp->str,
426  "SASL authentication failed; server %s said: %s",
427  session->namaddr, resp->str);
428  return (0);
429  }
430  return (1);
431 }
432 
433 /* smtp_sasl_cleanup - per-session cleanup */
434 
435 void smtp_sasl_cleanup(SMTP_SESSION *session)
436 {
437  if (session->sasl_username) {
438  myfree(session->sasl_username);
439  session->sasl_username = 0;
440  }
441  if (session->sasl_passwd) {
442  myfree(session->sasl_passwd);
443  session->sasl_passwd = 0;
444  }
445  if (session->sasl_mechanism_list) {
446  /* allocated in smtp_sasl_helo_auth */
447  myfree(session->sasl_mechanism_list);
448  session->sasl_mechanism_list = 0;
449  }
450  if (session->sasl_client) {
451  if (msg_verbose)
452  msg_info("disposing SASL state information");
453  xsasl_client_free(session->sasl_client);
454  session->sasl_client = 0;
455  }
456  if (session->sasl_reply) {
457  vstring_free(session->sasl_reply);
458  session->sasl_reply = 0;
459  }
460 }
461 
462 /* smtp_sasl_passivate - append serialized SASL attributes */
463 
464 void smtp_sasl_passivate(SMTP_SESSION *session, VSTRING *buf)
465 {
466 }
467 
468 /* smtp_sasl_activate - de-serialize SASL attributes */
469 
470 int smtp_sasl_activate(SMTP_SESSION *session, char *buf)
471 {
472  return (0);
473 }
474 
475 #endif
int msg_verbose
Definition: msg.c:177
VSTRING * dsn_buf
Definition: smtp.h:501
#define DSB_DEF_ACTION
Definition: dsn_buf.h:40
void myfree(void *ptr)
Definition: mymalloc.c:207
int smtp_sasl_passwd_lookup(SMTP_SESSION *)
char * str
Definition: smtp.h:500
char * var_procname
Definition: mail_params.c:252
char * mystrdup(const char *str)
Definition: mymalloc.c:225
XSASL_CLIENT_IMPL * xsasl_client_init(const char *, const char *)
Definition: xsasl_client.c:218
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
const char * dsn
Definition: smtp.h:499
DSN_BUF * dsb_update(DSN_BUF *dsb, const char *status, const char *action, const char *mtype, const char *mname, const char *dtype, const char *dtext, const char *format,...)
Definition: dsn_buf.c:239
int smtp_mode
Definition: smtp.c:963
char * var_smtp_sasl_passwd
Definition: smtp.c:862
#define STRING_LIST
Definition: string_list.h:22
DELIVER_REQUEST * request
Definition: smtp.h:146
#define vstream_longjmp(stream, val)
Definition: vstream.h:249
Definition: maps.h:22
void smtp_sasl_passivate(SMTP_SESSION *, VSTRING *)
#define LEN
Definition: cleanup_addr.c:106
#define DSB_SKIP_RMTA
Definition: dsn_buf.h:42
#define DICT_FLAG_UTF8_REQUEST
Definition: dict.h:130
char * mystrtok(char **src, const char *sep)
Definition: mystrtok.c:54
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
VSTRING * dest
Definition: smtp.h:53
void smtp_sasl_start(SMTP_SESSION *, const char *, const char *)
#define string_list_init(o, f, p)
Definition: string_list.h:24
#define xsasl_client_first(client, server, method, user, pass, init_resp)
Definition: xsasl.h:87
#define VAR_SMTP_SASL_MECHS
Definition: mail_params.h:1686
char * var_smtp_sasl_path
Definition: smtp.c:861
int var_smtp_sasl_auth_cache_time
Definition: smtp.c:957
#define DSB_MTYPE_DNS
Definition: dsn_buf.h:44
MAPS * maps_create(const char *title, const char *map_names, int dict_flags)
Definition: maps.c:112
VSTRING * host
Definition: smtp.h:54
#define mail_addr_find(maps, address, extension)
char * title
Definition: maps.h:23
char * var_smtp_sasl_mechs
Definition: smtp.c:864
#define DICT_FLAG_LOCK
Definition: dict.h:116
#define STR(x)
Definition: anvil.c:518
void msg_warn(const char *fmt,...)
Definition: msg.c:215
int smtp_sasl_activate(SMTP_SESSION *, char *)
VSTREAM * stream
Definition: smtp.h:305
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define XSASL_AUTH_OK
Definition: xsasl.h:123
#define VAR_LMTP_SMTP(x)
Definition: smtp.h:671
void smtp_sasl_connect(SMTP_SESSION *)
bool var_smtp_sender_auth
Definition: smtp.c:934
DSN_BUF * dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...)
Definition: dsn_buf.c:275
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
void smtp_sasl_initialize(void)
int smtp_sasl_authenticate(SMTP_SESSION *, DSN_BUF *)
char * var_smtp_sasl_auth_cache_name
Definition: smtp.c:956
int error
Definition: maps.h:25
#define xsasl_client_free(client)
Definition: xsasl.h:86
char * var_smtp_sasl_type
Definition: smtp.c:865
#define XSASL_CLIENT_CREATE(impl, args, a1, a2, a3, a4)
Definition: xsasl.h:115
#define xsasl_client_next(client, request, reply)
Definition: xsasl.h:89
SMTP_STATE * state
Definition: smtp.h:346
#define SMTP_ERR_DATA
Definition: smtp_stream.h:34
void smtp_chat_cmd(SMTP_SESSION *session, const char *fmt,...)
Definition: smtp_chat.c:181
void smtp_sasl_cleanup(SMTP_SESSION *)
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
int code
Definition: smtp.h:498
char * namaddr
Definition: smtp.h:307
SMTP_RESP * smtp_chat_resp(SMTP_SESSION *)
Definition: smtp_chat.c:235
bool var_smtp_sasl_auth_soft_bounce
Definition: smtp.c:958
const char * maps_find(MAPS *maps, const char *name, int flags)
Definition: maps.c:162
char * namaddrport
Definition: smtp.h:310
#define MATCH_FLAG_NONE
Definition: match_list.h:38
STRING_LIST * smtp_sasl_mechs
void msg_info(const char *fmt,...)
Definition: msg.c:199
SMTP_ITERATOR * iterator
Definition: smtp.h:306
#define DSB_DTYPE_SASL
Definition: dsn_buf.h:50