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