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 #include <sasl_mech_filter.h>
141 #include <string_list.h>
142
143 /* XSASL library. */
144
145 #include <xsasl.h>
146
147 /* Application-specific. */
148
149 #include "smtpd.h"
150 #include "smtpd_sasl_glue.h"
151 #include "smtpd_chat.h"
152
153 #ifdef USE_SASL_AUTH
154
155 /*
156 * SASL mechanism filter.
157 */
158 static STRING_LIST *smtpd_sasl_mech_filter;
159
160 /*
161 * Silly little macros.
162 */
163 #define STR(s) vstring_str(s)
164
165 /*
166 * SASL server implementation handle.
167 */
168 static XSASL_SERVER_IMPL *smtpd_sasl_impl;
169
170 /* smtpd_sasl_initialize - per-process initialization */
171
smtpd_sasl_initialize(void)172 void smtpd_sasl_initialize(void)
173 {
174
175 /*
176 * Sanity check.
177 */
178 if (smtpd_sasl_impl)
179 msg_panic("smtpd_sasl_initialize: repeated call");
180
181 /*
182 * Initialize the SASL library.
183 */
184 if ((smtpd_sasl_impl = xsasl_server_init(var_smtpd_sasl_type,
185 var_smtpd_sasl_path)) == 0)
186 msg_fatal("SASL per-process initialization failed");
187
188 /*
189 * Initialize the SASL mechanism filter.
190 */
191 smtpd_sasl_mech_filter = string_list_init(VAR_SMTPD_SASL_MECH_FILTER,
192 MATCH_FLAG_NONE,
193 var_smtpd_sasl_mech_filter);
194 }
195
196 /* smtpd_sasl_activate - per-connection initialization */
197
smtpd_sasl_activate(SMTPD_STATE * state,const char * sasl_opts_name,const char * sasl_opts_val)198 void smtpd_sasl_activate(SMTPD_STATE *state, const char *sasl_opts_name,
199 const char *sasl_opts_val)
200 {
201 const char *mechanism_list;
202 const char *filtered_mechanism_list;
203 XSASL_SERVER_CREATE_ARGS create_args;
204 int tls_flag;
205
206 /*
207 * Sanity check.
208 */
209 if (smtpd_sasl_is_active(state))
210 msg_panic("smtpd_sasl_activate: already active");
211
212 /*
213 * Initialize SASL-specific state variables. Use long-lived storage for
214 * base 64 conversion results, rather than local variables, to avoid
215 * memory leaks when a read or write routine returns abnormally after
216 * timeout or I/O error.
217 */
218 state->sasl_reply = vstring_alloc(20);
219 state->sasl_mechanism_list = 0;
220
221 /*
222 * Set up a new server context for this connection.
223 */
224 #ifdef USE_TLS
225 tls_flag = state->tls_context != 0;
226 #else
227 tls_flag = 0;
228 #endif
229 #define ADDR_OR_EMPTY(addr, unknown) (strcmp(addr, unknown) ? addr : "")
230 #define REALM_OR_NULL(realm) (*(realm) ? (realm) : (char *) 0)
231
232 if ((state->sasl_server =
233 XSASL_SERVER_CREATE(smtpd_sasl_impl, &create_args,
234 stream = state->client,
235 addr_family = state->addr_family,
236 server_addr = ADDR_OR_EMPTY(state->dest_addr,
237 SERVER_ADDR_UNKNOWN),
238 server_port = ADDR_OR_EMPTY(state->dest_port,
239 SERVER_PORT_UNKNOWN),
240 client_addr = ADDR_OR_EMPTY(state->addr,
241 CLIENT_ADDR_UNKNOWN),
242 client_port = ADDR_OR_EMPTY(state->port,
243 CLIENT_PORT_UNKNOWN),
244 service = var_smtpd_sasl_service,
245 user_realm = REALM_OR_NULL(var_smtpd_sasl_realm),
246 security_options = sasl_opts_val,
247 tls_flag = tls_flag)) == 0)
248 msg_fatal("SASL per-connection initialization failed");
249
250 /*
251 * Get the list of authentication mechanisms.
252 */
253 if ((mechanism_list =
254 xsasl_server_get_mechanism_list(state->sasl_server)) == 0)
255 msg_fatal("no SASL authentication mechanisms");
256 filtered_mechanism_list =
257 sasl_mech_filter(smtpd_sasl_mech_filter, mechanism_list);
258 if (*filtered_mechanism_list == 0)
259 msg_fatal("%s discards all mechanisms in '%s'",
260 VAR_SMTPD_SASL_MECH_FILTER, mechanism_list);
261 state->sasl_mechanism_list = mystrdup(filtered_mechanism_list);
262 }
263
264 /* smtpd_sasl_state_init - initialize state to allow extern authentication. */
265
smtpd_sasl_state_init(SMTPD_STATE * state)266 void smtpd_sasl_state_init(SMTPD_STATE *state)
267 {
268 /* Initialization to support external authentication (e.g., XCLIENT). */
269 state->sasl_username = 0;
270 state->sasl_method = 0;
271 state->sasl_sender = 0;
272 }
273
274 /* smtpd_sasl_deactivate - per-connection cleanup */
275
smtpd_sasl_deactivate(SMTPD_STATE * state)276 void smtpd_sasl_deactivate(SMTPD_STATE *state)
277 {
278 if (state->sasl_reply) {
279 vstring_free(state->sasl_reply);
280 state->sasl_reply = 0;
281 }
282 if (state->sasl_mechanism_list) {
283 myfree(state->sasl_mechanism_list);
284 state->sasl_mechanism_list = 0;
285 }
286 if (state->sasl_username) {
287 myfree(state->sasl_username);
288 state->sasl_username = 0;
289 }
290 if (state->sasl_method) {
291 myfree(state->sasl_method);
292 state->sasl_method = 0;
293 }
294 if (state->sasl_sender) {
295 myfree(state->sasl_sender);
296 state->sasl_sender = 0;
297 }
298 if (state->sasl_server) {
299 xsasl_server_free(state->sasl_server);
300 state->sasl_server = 0;
301 }
302 }
303
304 /* smtpd_sasl_authenticate - per-session authentication */
305
smtpd_sasl_authenticate(SMTPD_STATE * state,const char * sasl_method,const char * init_response)306 int smtpd_sasl_authenticate(SMTPD_STATE *state,
307 const char *sasl_method,
308 const char *init_response)
309 {
310 int status;
311 const char *sasl_username;
312
313 /*
314 * SASL authentication protocol start-up. Process any initial client
315 * response that was sent along in the AUTH command.
316 */
317 for (status = xsasl_server_first(state->sasl_server, sasl_method,
318 init_response, state->sasl_reply);
319 status == XSASL_AUTH_MORE;
320 status = xsasl_server_next(state->sasl_server, STR(state->buffer),
321 state->sasl_reply)) {
322
323 /*
324 * Send a server challenge.
325 */
326 smtpd_chat_reply(state, "334 %s", STR(state->sasl_reply));
327
328 /*
329 * Receive the client response. "*" means that the client gives up.
330 */
331 if (!smtpd_chat_query_limit(state, var_smtpd_sasl_resp_limit)) {
332 smtpd_chat_reply(state, "500 5.5.6 SASL response limit exceeded");
333 return (-1);
334 }
335 if (strcmp(STR(state->buffer), "*") == 0) {
336 msg_warn("%s: SASL %s authentication aborted",
337 state->namaddr, sasl_method);
338 smtpd_chat_reply(state, "501 5.7.0 Authentication aborted");
339 return (-1);
340 }
341 }
342 if (status != XSASL_AUTH_DONE) {
343 msg_warn("%s: SASL %s authentication failed: %s",
344 state->namaddr, sasl_method,
345 STR(state->sasl_reply));
346 /* RFC 4954 Section 6. */
347 if (status == XSASL_AUTH_TEMP)
348 smtpd_chat_reply(state, "454 4.7.0 Temporary authentication failure: %s",
349 STR(state->sasl_reply));
350 else
351 smtpd_chat_reply(state, "535 5.7.8 Error: authentication failed: %s",
352 STR(state->sasl_reply));
353 return (-1);
354 }
355 /* RFC 4954 Section 6. */
356 smtpd_chat_reply(state, "235 2.7.0 Authentication successful");
357 if ((sasl_username = xsasl_server_get_username(state->sasl_server)) == 0)
358 msg_panic("cannot look up the authenticated SASL username");
359 state->sasl_username = mystrdup(sasl_username);
360 printable(state->sasl_username, '?');
361 state->sasl_method = mystrdup(sasl_method);
362 printable(state->sasl_method, '?');
363
364 return (0);
365 }
366
367 /* smtpd_sasl_logout - clean up after smtpd_sasl_authenticate */
368
smtpd_sasl_logout(SMTPD_STATE * state)369 void smtpd_sasl_logout(SMTPD_STATE *state)
370 {
371 if (state->sasl_username) {
372 myfree(state->sasl_username);
373 state->sasl_username = 0;
374 }
375 if (state->sasl_method) {
376 myfree(state->sasl_method);
377 state->sasl_method = 0;
378 }
379 }
380
381 /* smtpd_sasl_login - set login information */
382
smtpd_sasl_login(SMTPD_STATE * state,const char * sasl_username,const char * sasl_method)383 void smtpd_sasl_login(SMTPD_STATE *state, const char *sasl_username,
384 const char *sasl_method)
385 {
386 if (state->sasl_username)
387 myfree(state->sasl_username);
388 state->sasl_username = mystrdup(sasl_username);
389 if (state->sasl_method)
390 myfree(state->sasl_method);
391 state->sasl_method = mystrdup(sasl_method);
392 }
393
394 #endif
395