7 /* Postfix SMTP server, SASL support interface
9 /* #include "smtpd_sasl_glue.h"
11 /* void smtpd_sasl_initialize()
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;
18 /* char *smtpd_sasl_authenticate(state, sasl_method, init_response)
19 /* SMTPD_STATE *state;
20 /* const char *sasl_method;
21 /* const char *init_response;
23 /* void smtpd_sasl_logout(state)
24 /* SMTPD_STATE *state;
26 /* void smtpd_sasl_deactivate(state)
27 /* SMTPD_STATE *state;
29 /* int smtpd_sasl_is_active(state)
30 /* SMTPD_STATE *state;
32 /* int smtpd_sasl_set_inactive(state)
33 /* SMTPD_STATE *state;
35 /* This module encapsulates most of the detail specific to SASL
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.
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.
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:
54 /* The authentication method that was successfully applied.
55 /* This member is a null pointer in the absence of successful
58 /* The username that was successfully authenticated.
59 /* This member is a null pointer in the absence of successful
62 /* smtpd_sasl_logout() cleans up after smtpd_sasl_authenticate().
63 /* This routine exists for the sake of symmetry.
65 /* smtpd_sasl_deactivate() performs per-connection cleanup.
66 /* This routine should be called at the end of every connection.
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().
72 /* smtpd_sasl_set_inactive() initializes the SMTP session
73 /* state before the first smtpd_sasl_activate() call.
77 /* SMTP session context.
79 /* Security options parameter name.
81 /* Security options parameter value.
83 /* A SASL mechanism name
85 /* An optional initial client response.
87 /* All errors are fatal.
91 /* The Secure Mailer license must be distributed with this software.
93 /* Initial implementation by:
96 /* 65760 Eschborn, Germany
100 /* IBM T.J. Watson Research
102 /* Yorktown Heights, NY 10598, USA
105 /* System library. */
107 #include <sys_defs.h>
111 /* Utility library. */
114 #include <mymalloc.h>
115 #include <stringops.h>
117 /* Global library. */
119 #include <mail_params.h>
125 /* Application-specific. */
128 #include "smtpd_sasl_glue.h"
129 #include "smtpd_chat.h"
134 * Silly little macros.
136 #define STR(s) vstring_str(s)
139 * SASL server implementation handle.
141 static XSASL_SERVER_IMPL
*smtpd_sasl_impl
;
143 /* smtpd_sasl_initialize - per-process initialization */
145 void smtpd_sasl_initialize(void)
152 msg_panic("smtpd_sasl_initialize: repeated call");
155 * Initialize the SASL library.
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");
163 /* smtpd_sasl_activate - per-connection initialization */
165 void smtpd_sasl_activate(SMTPD_STATE
*state
, const char *sasl_opts_name
,
166 const char *sasl_opts_val
)
168 const char *mechanism_list
;
169 XSASL_SERVER_CREATE_ARGS create_args
;
175 if (smtpd_sasl_is_active(state
))
176 msg_panic("smtpd_sasl_activate: already active");
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.
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;
191 * Set up a new server context for this connection.
193 #define SMTPD_SASL_SERVICE "smtp"
195 tls_flag
= state
->tls_context
!= 0;
199 #define ADDR_OR_EMPTY(addr, unknown) (strcmp(addr, unknown) ? addr : "")
200 #define REALM_OR_NULL(realm) (*(realm) ? (realm) : (char *) 0)
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");
215 * Get the list of authentication mechanisms.
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
);
223 /* smtpd_sasl_deactivate - per-connection cleanup */
225 void smtpd_sasl_deactivate(SMTPD_STATE
*state
)
227 if (state
->sasl_reply
) {
228 vstring_free(state
->sasl_reply
);
229 state
->sasl_reply
= 0;
231 if (state
->sasl_mechanism_list
) {
232 myfree(state
->sasl_mechanism_list
);
233 state
->sasl_mechanism_list
= 0;
235 if (state
->sasl_username
) {
236 myfree(state
->sasl_username
);
237 state
->sasl_username
= 0;
239 if (state
->sasl_method
) {
240 myfree(state
->sasl_method
);
241 state
->sasl_method
= 0;
243 if (state
->sasl_sender
) {
244 myfree(state
->sasl_sender
);
245 state
->sasl_sender
= 0;
247 if (state
->sasl_server
) {
248 xsasl_server_free(state
->sasl_server
);
249 state
->sasl_server
= 0;
253 /* smtpd_sasl_authenticate - per-session authentication */
255 int smtpd_sasl_authenticate(SMTPD_STATE
*state
,
256 const char *sasl_method
,
257 const char *init_response
)
260 const char *sasl_username
;
263 * SASL authentication protocol start-up. Process any initial client
264 * response that was sent along in the AUTH command.
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
)) {
273 * Send a server challenge.
275 smtpd_chat_reply(state
, "334 %s", STR(state
->sasl_reply
));
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
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");
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
));
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
, '?');
313 /* smtpd_sasl_logout - clean up after smtpd_sasl_authenticate */
315 void smtpd_sasl_logout(SMTPD_STATE
*state
)
317 if (state
->sasl_username
) {
318 myfree(state
->sasl_username
);
319 state
->sasl_username
= 0;
321 if (state
->sasl_method
) {
322 myfree(state
->sasl_method
);
323 state
->sasl_method
= 0;