xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtpd/smtpd_sasl_glue.c (revision 200d779b75dbeafa7bc01fd0f60bc61185f6967b)
1 /*	$NetBSD: smtpd_sasl_glue.c,v 1.1.1.4 2014/07/06 19:27:57 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 #ifdef USE_TLS
208     tls_flag = state->tls_context != 0;
209 #else
210     tls_flag = 0;
211 #endif
212 #define ADDR_OR_EMPTY(addr, unknown) (strcmp(addr, unknown) ? addr : "")
213 #define REALM_OR_NULL(realm) (*(realm) ? (realm) : (char *) 0)
214 
215     if ((state->sasl_server =
216 	 XSASL_SERVER_CREATE(smtpd_sasl_impl, &create_args,
217 			     stream = state->client,
218 			     server_addr = (state->dest_addr ?
219 					    state->dest_addr : ""),
220 			     client_addr = ADDR_OR_EMPTY(state->addr,
221 						       CLIENT_ADDR_UNKNOWN),
222 			     service = var_smtpd_sasl_service,
223 			   user_realm = REALM_OR_NULL(var_smtpd_sasl_realm),
224 			     security_options = sasl_opts_val,
225 			     tls_flag = tls_flag)) == 0)
226 	msg_fatal("SASL per-connection initialization failed");
227 
228     /*
229      * Get the list of authentication mechanisms.
230      */
231     if ((mechanism_list =
232 	 xsasl_server_get_mechanism_list(state->sasl_server)) == 0)
233 	msg_fatal("no SASL authentication mechanisms");
234     state->sasl_mechanism_list = mystrdup(mechanism_list);
235 }
236 
237 /* smtpd_sasl_state_init - initialize state to allow extern authentication. */
238 
239 void    smtpd_sasl_state_init(SMTPD_STATE *state)
240 {
241     /* Initialization to support external authentication (e.g., XCLIENT). */
242     state->sasl_username = 0;
243     state->sasl_method = 0;
244     state->sasl_sender = 0;
245 }
246 
247 /* smtpd_sasl_deactivate - per-connection cleanup */
248 
249 void    smtpd_sasl_deactivate(SMTPD_STATE *state)
250 {
251     if (state->sasl_reply) {
252 	vstring_free(state->sasl_reply);
253 	state->sasl_reply = 0;
254     }
255     if (state->sasl_mechanism_list) {
256 	myfree(state->sasl_mechanism_list);
257 	state->sasl_mechanism_list = 0;
258     }
259     if (state->sasl_username) {
260 	myfree(state->sasl_username);
261 	state->sasl_username = 0;
262     }
263     if (state->sasl_method) {
264 	myfree(state->sasl_method);
265 	state->sasl_method = 0;
266     }
267     if (state->sasl_sender) {
268 	myfree(state->sasl_sender);
269 	state->sasl_sender = 0;
270     }
271     if (state->sasl_server) {
272 	xsasl_server_free(state->sasl_server);
273 	state->sasl_server = 0;
274     }
275 }
276 
277 /* smtpd_sasl_authenticate - per-session authentication */
278 
279 int     smtpd_sasl_authenticate(SMTPD_STATE *state,
280 				        const char *sasl_method,
281 				        const char *init_response)
282 {
283     int     status;
284     const char *sasl_username;
285 
286     /*
287      * SASL authentication protocol start-up. Process any initial client
288      * response that was sent along in the AUTH command.
289      */
290     for (status = xsasl_server_first(state->sasl_server, sasl_method,
291 				     init_response, state->sasl_reply);
292 	 status == XSASL_AUTH_MORE;
293 	 status = xsasl_server_next(state->sasl_server, STR(state->buffer),
294 				    state->sasl_reply)) {
295 
296 	/*
297 	 * Send a server challenge.
298 	 */
299 	smtpd_chat_reply(state, "334 %s", STR(state->sasl_reply));
300 
301 	/*
302 	 * Receive the client response. "*" means that the client gives up.
303 	 * XXX For now we ignore the fact that an excessively long response
304 	 * will be chopped into multiple reponses. To handle such responses,
305 	 * we need to change smtpd_chat_query() so that it returns an error
306 	 * indication.
307 	 */
308 	smtpd_chat_query(state);
309 	if (strcmp(STR(state->buffer), "*") == 0) {
310 	    msg_warn("%s: SASL %s authentication aborted",
311 		     state->namaddr, sasl_method);
312 	    smtpd_chat_reply(state, "501 5.7.0 Authentication aborted");
313 	    return (-1);
314 	}
315     }
316     if (status != XSASL_AUTH_DONE) {
317 	msg_warn("%s: SASL %s authentication failed: %s",
318 		 state->namaddr, sasl_method,
319 		 STR(state->sasl_reply));
320 	/* RFC 4954 Section 6. */
321 	smtpd_chat_reply(state, "535 5.7.8 Error: authentication failed: %s",
322 			 STR(state->sasl_reply));
323 	return (-1);
324     }
325     /* RFC 4954 Section 6. */
326     smtpd_chat_reply(state, "235 2.7.0 Authentication successful");
327     if ((sasl_username = xsasl_server_get_username(state->sasl_server)) == 0)
328 	msg_panic("cannot look up the authenticated SASL username");
329     state->sasl_username = mystrdup(sasl_username);
330     printable(state->sasl_username, '?');
331     state->sasl_method = mystrdup(sasl_method);
332     printable(state->sasl_method, '?');
333 
334     return (0);
335 }
336 
337 /* smtpd_sasl_logout - clean up after smtpd_sasl_authenticate */
338 
339 void    smtpd_sasl_logout(SMTPD_STATE *state)
340 {
341     if (state->sasl_username) {
342 	myfree(state->sasl_username);
343 	state->sasl_username = 0;
344     }
345     if (state->sasl_method) {
346 	myfree(state->sasl_method);
347 	state->sasl_method = 0;
348     }
349 }
350 
351 /* smtpd_sasl_login - set login information */
352 
353 void    smtpd_sasl_login(SMTPD_STATE *state, const char *sasl_username,
354 			         const char *sasl_method)
355 {
356     if (state->sasl_username)
357 	myfree(state->sasl_username);
358     state->sasl_username = mystrdup(sasl_username);
359     if (state->sasl_method)
360 	myfree(state->sasl_method);
361     state->sasl_method = mystrdup(sasl_method);
362 }
363 
364 #endif
365