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