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