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