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