Postfix3.3.1
smtpd_sasl_glue.c
[詳解]
1 /*++
2 /* NAME
3 /* smtpd_sasl_glue 3
4 /* SUMMARY
5 /* Postfix SMTP server, SASL support interface
6 /* SYNOPSIS
7 /* #include "smtpd_sasl_glue.h"
8 /*
9 /* void smtpd_sasl_state_init(state)
10 /* SMTPD_STATE *state;
11 /*
12 /* void smtpd_sasl_initialize()
13 /*
14 /* void smtpd_sasl_activate(state, sasl_opts_name, sasl_opts_val)
15 /* SMTPD_STATE *state;
16 /* const char *sasl_opts_name;
17 /* const char *sasl_opts_val;
18 /*
19 /* char *smtpd_sasl_authenticate(state, sasl_method, init_response)
20 /* SMTPD_STATE *state;
21 /* const char *sasl_method;
22 /* const char *init_response;
23 /*
24 /* void smtpd_sasl_logout(state)
25 /* SMTPD_STATE *state;
26 /*
27 /* void smtpd_sasl_login(state, sasl_username, sasl_method)
28 /* SMTPD_STATE *state;
29 /* const char *sasl_username;
30 /* const char *sasl_method;
31 /*
32 /* void smtpd_sasl_deactivate(state)
33 /* SMTPD_STATE *state;
34 /*
35 /* int smtpd_sasl_is_active(state)
36 /* SMTPD_STATE *state;
37 /*
38 /* int smtpd_sasl_set_inactive(state)
39 /* SMTPD_STATE *state;
40 /* DESCRIPTION
41 /* This module encapsulates most of the detail specific to SASL
42 /* authentication.
43 /*
44 /* smtpd_sasl_state_init() performs minimal server state
45 /* initialization to support external authentication (e.g.,
46 /* XCLIENT) without having to enable SASL in main.cf. This
47 /* should always be called at process startup.
48 /*
49 /* smtpd_sasl_initialize() initializes the SASL library. This
50 /* routine should be called once at process start-up. It may
51 /* need access to the file system for run-time loading of
52 /* plug-in modules. There is no corresponding cleanup routine.
53 /*
54 /* smtpd_sasl_activate() performs per-connection initialization.
55 /* This routine should be called once at the start of every
56 /* connection. The sasl_opts_name and sasl_opts_val parameters
57 /* are the postfix configuration parameters setting the security
58 /* policy of the SASL authentication.
59 /*
60 /* smtpd_sasl_authenticate() implements the authentication
61 /* dialog. The result is zero in case of success, -1 in case
62 /* of failure. smtpd_sasl_authenticate() updates the following
63 /* state structure members:
64 /* .IP sasl_method
65 /* The authentication method that was successfully applied.
66 /* This member is a null pointer in the absence of successful
67 /* authentication.
68 /* .IP sasl_username
69 /* The username that was successfully authenticated.
70 /* This member is a null pointer in the absence of successful
71 /* authentication.
72 /* .PP
73 /* smtpd_sasl_login() records the result of successful external
74 /* authentication, i.e. without invoking smtpd_sasl_authenticate(),
75 /* but produces an otherwise equivalent result.
76 /*
77 /* smtpd_sasl_logout() cleans up after smtpd_sasl_authenticate().
78 /* This routine exists for the sake of symmetry.
79 /*
80 /* smtpd_sasl_deactivate() performs per-connection cleanup.
81 /* This routine should be called at the end of every connection.
82 /*
83 /* smtpd_sasl_is_active() is a predicate that returns true
84 /* if the SMTP server session state is between smtpd_sasl_activate()
85 /* and smtpd_sasl_deactivate().
86 /*
87 /* smtpd_sasl_set_inactive() initializes the SMTP session
88 /* state before the first smtpd_sasl_activate() call.
89 /*
90 /* Arguments:
91 /* .IP state
92 /* SMTP session context.
93 /* .IP sasl_opts_name
94 /* Security options parameter name.
95 /* .IP sasl_opts_val
96 /* Security options parameter value.
97 /* .IP sasl_method
98 /* A SASL mechanism name
99 /* .IP init_reply
100 /* An optional initial client response.
101 /* DIAGNOSTICS
102 /* All errors are fatal.
103 /* LICENSE
104 /* .ad
105 /* .fi
106 /* The Secure Mailer license must be distributed with this software.
107 /* AUTHOR(S)
108 /* Initial implementation by:
109 /* Till Franke
110 /* SuSE Rhein/Main AG
111 /* 65760 Eschborn, Germany
112 /*
113 /* Adopted by:
114 /* Wietse Venema
115 /* IBM T.J. Watson Research
116 /* P.O. Box 704
117 /* Yorktown Heights, NY 10598, USA
118 /*
119 /* Wietse Venema
120 /* Google, Inc.
121 /* 111 8th Avenue
122 /* New York, NY 10011, USA
123 /*--*/
124 
125 /* System library. */
126 
127 #include <sys_defs.h>
128 #include <stdlib.h>
129 #include <string.h>
130 
131 /* Utility library. */
132 
133 #include <msg.h>
134 #include <mymalloc.h>
135 #include <stringops.h>
136 
137 /* Global library. */
138 
139 #include <mail_params.h>
140 
141 /* XSASL library. */
142 
143 #include <xsasl.h>
144 
145 /* Application-specific. */
146 
147 #include "smtpd.h"
148 #include "smtpd_sasl_glue.h"
149 #include "smtpd_chat.h"
150 
151 #ifdef USE_SASL_AUTH
152 
153 /*
154  * Silly little macros.
155  */
156 #define STR(s) vstring_str(s)
157 
158  /*
159  * SASL server implementation handle.
160  */
161 static XSASL_SERVER_IMPL *smtpd_sasl_impl;
162 
163 /* smtpd_sasl_initialize - per-process initialization */
164 
165 void smtpd_sasl_initialize(void)
166 {
167 
168  /*
169  * Sanity check.
170  */
171  if (smtpd_sasl_impl)
172  msg_panic("smtpd_sasl_initialize: repeated call");
173 
174  /*
175  * Initialize the SASL library.
176  */
177  if ((smtpd_sasl_impl = xsasl_server_init(var_smtpd_sasl_type,
178  var_smtpd_sasl_path)) == 0)
179  msg_fatal("SASL per-process initialization failed");
180 
181 }
182 
183 /* smtpd_sasl_activate - per-connection initialization */
184 
185 void smtpd_sasl_activate(SMTPD_STATE *state, const char *sasl_opts_name,
186  const char *sasl_opts_val)
187 {
188  const char *mechanism_list;
189  XSASL_SERVER_CREATE_ARGS create_args;
190  int tls_flag;
191 
192  /*
193  * Sanity check.
194  */
195  if (smtpd_sasl_is_active(state))
196  msg_panic("smtpd_sasl_activate: already active");
197 
198  /*
199  * Initialize SASL-specific state variables. Use long-lived storage for
200  * base 64 conversion results, rather than local variables, to avoid
201  * memory leaks when a read or write routine returns abnormally after
202  * timeout or I/O error.
203  */
204  state->sasl_reply = vstring_alloc(20);
205  state->sasl_mechanism_list = 0;
206 
207  /*
208  * Set up a new server context for this connection.
209  */
210 #ifdef USE_TLS
211  tls_flag = state->tls_context != 0;
212 #else
213  tls_flag = 0;
214 #endif
215 #define ADDR_OR_EMPTY(addr, unknown) (strcmp(addr, unknown) ? addr : "")
216 #define REALM_OR_NULL(realm) (*(realm) ? (realm) : (char *) 0)
217 
218  if ((state->sasl_server =
219  XSASL_SERVER_CREATE(smtpd_sasl_impl, &create_args,
220  stream = state->client,
221  addr_family = state->addr_family,
222  server_addr = ADDR_OR_EMPTY(state->dest_addr,
224  server_port = ADDR_OR_EMPTY(state->dest_port,
226  client_addr = ADDR_OR_EMPTY(state->addr,
228  client_port = ADDR_OR_EMPTY(state->port,
230  service = var_smtpd_sasl_service,
231  user_realm = REALM_OR_NULL(var_smtpd_sasl_realm),
232  security_options = sasl_opts_val,
233  tls_flag = tls_flag)) == 0)
234  msg_fatal("SASL per-connection initialization failed");
235 
236  /*
237  * Get the list of authentication mechanisms.
238  */
239  if ((mechanism_list =
240  xsasl_server_get_mechanism_list(state->sasl_server)) == 0)
241  msg_fatal("no SASL authentication mechanisms");
242  state->sasl_mechanism_list = mystrdup(mechanism_list);
243 }
244 
245 /* smtpd_sasl_state_init - initialize state to allow extern authentication. */
246 
248 {
249  /* Initialization to support external authentication (e.g., XCLIENT). */
250  state->sasl_username = 0;
251  state->sasl_method = 0;
252  state->sasl_sender = 0;
253 }
254 
255 /* smtpd_sasl_deactivate - per-connection cleanup */
256 
258 {
259  if (state->sasl_reply) {
260  vstring_free(state->sasl_reply);
261  state->sasl_reply = 0;
262  }
263  if (state->sasl_mechanism_list) {
264  myfree(state->sasl_mechanism_list);
265  state->sasl_mechanism_list = 0;
266  }
267  if (state->sasl_username) {
268  myfree(state->sasl_username);
269  state->sasl_username = 0;
270  }
271  if (state->sasl_method) {
272  myfree(state->sasl_method);
273  state->sasl_method = 0;
274  }
275  if (state->sasl_sender) {
276  myfree(state->sasl_sender);
277  state->sasl_sender = 0;
278  }
279  if (state->sasl_server) {
280  xsasl_server_free(state->sasl_server);
281  state->sasl_server = 0;
282  }
283 }
284 
285 /* smtpd_sasl_authenticate - per-session authentication */
286 
288  const char *sasl_method,
289  const char *init_response)
290 {
291  int status;
292  const char *sasl_username;
293 
294  /*
295  * SASL authentication protocol start-up. Process any initial client
296  * response that was sent along in the AUTH command.
297  */
298  for (status = xsasl_server_first(state->sasl_server, sasl_method,
299  init_response, state->sasl_reply);
300  status == XSASL_AUTH_MORE;
301  status = xsasl_server_next(state->sasl_server, STR(state->buffer),
302  state->sasl_reply)) {
303 
304  /*
305  * Send a server challenge.
306  */
307  smtpd_chat_reply(state, "334 %s", STR(state->sasl_reply));
308 
309  /*
310  * Receive the client response. "*" means that the client gives up.
311  * XXX For now we ignore the fact that an excessively long response
312  * will be chopped into multiple responses. To handle such responses,
313  * we need to change smtpd_chat_query() so that it returns an error
314  * indication.
315  */
316  smtpd_chat_query(state);
317  if (strcmp(STR(state->buffer), "*") == 0) {
318  msg_warn("%s: SASL %s authentication aborted",
319  state->namaddr, sasl_method);
320  smtpd_chat_reply(state, "501 5.7.0 Authentication aborted");
321  return (-1);
322  }
323  }
324  if (status != XSASL_AUTH_DONE) {
325  msg_warn("%s: SASL %s authentication failed: %s",
326  state->namaddr, sasl_method,
327  STR(state->sasl_reply));
328  /* RFC 4954 Section 6. */
329  if (status == XSASL_AUTH_TEMP)
330  smtpd_chat_reply(state, "454 4.7.0 Temporary authentication failure: %s",
331  STR(state->sasl_reply));
332  else
333  smtpd_chat_reply(state, "535 5.7.8 Error: authentication failed: %s",
334  STR(state->sasl_reply));
335  return (-1);
336  }
337  /* RFC 4954 Section 6. */
338  smtpd_chat_reply(state, "235 2.7.0 Authentication successful");
339  if ((sasl_username = xsasl_server_get_username(state->sasl_server)) == 0)
340  msg_panic("cannot look up the authenticated SASL username");
341  state->sasl_username = mystrdup(sasl_username);
342  printable(state->sasl_username, '?');
343  state->sasl_method = mystrdup(sasl_method);
344  printable(state->sasl_method, '?');
345 
346  return (0);
347 }
348 
349 /* smtpd_sasl_logout - clean up after smtpd_sasl_authenticate */
350 
351 void smtpd_sasl_logout(SMTPD_STATE *state)
352 {
353  if (state->sasl_username) {
354  myfree(state->sasl_username);
355  state->sasl_username = 0;
356  }
357  if (state->sasl_method) {
358  myfree(state->sasl_method);
359  state->sasl_method = 0;
360  }
361 }
362 
363 /* smtpd_sasl_login - set login information */
364 
365 void smtpd_sasl_login(SMTPD_STATE *state, const char *sasl_username,
366  const char *sasl_method)
367 {
368  if (state->sasl_username)
369  myfree(state->sasl_username);
370  state->sasl_username = mystrdup(sasl_username);
371  if (state->sasl_method)
372  myfree(state->sasl_method);
373  state->sasl_method = mystrdup(sasl_method);
374 }
375 
376 #endif
char * var_smtpd_sasl_path
Definition: smtpd.c:1277
#define xsasl_server_get_username(server)
Definition: xsasl.h:40
void myfree(void *ptr)
Definition: mymalloc.c:207
void smtpd_sasl_initialize(void)
char * mystrdup(const char *str)
Definition: mymalloc.c:225
char * dest_port
Definition: smtpd.h:83
VSTRING * buffer
Definition: smtpd.h:71
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define xsasl_server_free(server)
Definition: xsasl.h:33
#define smtpd_sasl_is_active(s)
#define SERVER_PORT_UNKNOWN
Definition: smtpd.h:289
#define SERVER_ADDR_UNKNOWN
Definition: smtpd.h:288
int addr_family
Definition: smtpd.h:81
VSTREAM * client
Definition: smtpd.h:70
#define XSASL_AUTH_DONE
Definition: xsasl.h:125
#define xsasl_server_get_mechanism_list(server)
Definition: xsasl.h:38
void smtpd_sasl_deactivate(SMTPD_STATE *)
void smtpd_sasl_state_init(SMTPD_STATE *)
int smtpd_sasl_authenticate(SMTPD_STATE *, const char *, const char *)
char * port
Definition: smtpd.h:78
void smtpd_sasl_logout(SMTPD_STATE *)
#define STR(x)
Definition: anvil.c:518
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
XSASL_SERVER_IMPL * xsasl_server_init(const char *, const char *)
Definition: xsasl_server.c:248
#define XSASL_AUTH_TEMP
Definition: xsasl.h:128
void smtpd_chat_reply(SMTPD_STATE *state, const char *format,...)
Definition: smtpd_chat.c:148
#define xsasl_server_next(server, request, reply)
Definition: xsasl.h:36
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
void smtpd_chat_query(SMTPD_STATE *state)
Definition: smtpd_chat.c:126
#define XSASL_AUTH_MORE
Definition: xsasl.h:124
char * dest_addr
Definition: smtpd.h:82
#define CLIENT_ADDR_UNKNOWN
Definition: qmqpd.h:63
char * addr
Definition: smtpd.h:77
char * var_smtpd_sasl_type
Definition: smtpd.c:1282
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define XSASL_SERVER_CREATE(impl, args, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
Definition: xsasl.h:70
char * printable(char *string, int replacement)
Definition: printable.c:49
void smtpd_sasl_activate(SMTPD_STATE *, const char *, const char *)
#define CLIENT_PORT_UNKNOWN
Definition: qmqpd.h:64
#define xsasl_server_first(server, method, init_resp, reply)
Definition: xsasl.h:34
void smtpd_sasl_login(SMTPD_STATE *, const char *, const char *)
char * namaddr
Definition: smtpd.h:79
char * var_smtpd_sasl_realm
Definition: smtpd.c:1280
char * var_smtpd_sasl_service
Definition: smtpd.c:1278