xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtp/smtp_sasl_proto.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: smtp_sasl_proto.c,v 1.2 2017/02/14 01:16:48 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	smtp_sasl_proto 3
6 /* SUMMARY
7 /*	Postfix SASL interface for SMTP client
8 /* SYNOPSIS
9 /*	#include smtp_sasl.h
10 /*
11 /*	void	smtp_sasl_helo_auth(state, words)
12 /*	SMTP_STATE *state;
13 /*	const char *words;
14 /*
15 /*	int	smtp_sasl_helo_login(state)
16 /*	SMTP_STATE *state;
17 /* DESCRIPTION
18 /*	This module contains random chunks of code that implement
19 /*	the SMTP protocol interface for SASL negotiation. The goal
20 /*	is to reduce clutter in the main SMTP client source code.
21 /*
22 /*	smtp_sasl_helo_auth() processes the AUTH option in the
23 /*	SMTP server's EHLO response.
24 /*
25 /*	smtp_sasl_helo_login() authenticates the SMTP client to the
26 /*	SMTP server, using the authentication mechanism information
27 /*	given by the server. The result is a Postfix delivery status
28 /*	code in case of trouble.
29 /*
30 /*	Arguments:
31 /* .IP state
32 /*	Session context.
33 /* .IP words
34 /*	List of SASL authentication mechanisms (separated by blanks)
35 /* DIAGNOSTICS
36 /*	All errors are fatal.
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /*	The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /*	Original author:
43 /*	Till Franke
44 /*	SuSE Rhein/Main AG
45 /*	65760 Eschborn, Germany
46 /*
47 /*	Adopted by:
48 /*	Wietse Venema
49 /*	IBM T.J. Watson Research
50 /*	P.O. Box 704
51 /*	Yorktown Heights, NY 10598, USA
52 /*--*/
53 
54 /* System library. */
55 
56 #include <sys_defs.h>
57 #include <string.h>
58 #ifdef STRCASECMP_IN_STRINGS_H
59 #include <strings.h>
60 #endif
61 
62 /* Utility library. */
63 
64 #include <msg.h>
65 #include <mymalloc.h>
66 #include <stringops.h>
67 
68 /* Global library. */
69 
70 #include <mail_params.h>
71 
72 /* Application-specific. */
73 
74 #include "smtp.h"
75 #include "smtp_sasl.h"
76 
77 #ifdef USE_SASL_AUTH
78 
79 /* smtp_sasl_compat_mechs - Trim server's mechanism list */
80 
81 static const char *smtp_sasl_compat_mechs(const char *words)
82 {
83     static VSTRING *buf;
84     char   *mech_list;
85     char   *save_mech;
86     char   *mech;
87 
88     /*
89      * Use server's mechanisms if no filter specified
90      */
91     if (smtp_sasl_mechs == 0 || *words == 0)
92 	return (words);
93 
94     if (buf == 0)
95 	buf = vstring_alloc(10);
96 
97     VSTRING_RESET(buf);
98     VSTRING_TERMINATE(buf);
99 
100     save_mech = mech_list = mystrdup(words);
101 
102     while ((mech = mystrtok(&mech_list, " \t")) != 0) {
103 	if (string_list_match(smtp_sasl_mechs, mech)) {
104 	    if (VSTRING_LEN(buf) > 0)
105 		VSTRING_ADDCH(buf, ' ');
106 	    vstring_strcat(buf, mech);
107 	}
108     }
109     myfree(save_mech);
110 
111     return (vstring_str(buf));
112 }
113 
114 /* smtp_sasl_helo_auth - handle AUTH option in EHLO reply */
115 
116 void    smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words)
117 {
118     const char *mech_list = smtp_sasl_compat_mechs(words);
119     char   *junk;
120 
121     /*
122      * XXX If the server offers no compatible authentication mechanisms, then
123      * pretend that the server doesn't support SASL authentication.
124      *
125      * XXX If the server offers multiple different lists, concatenate them. Let
126      * the SASL library worry about duplicates.
127      */
128     if (session->sasl_mechanism_list) {
129 	if (strcasecmp(session->sasl_mechanism_list, mech_list) != 0
130 	    && strlen(mech_list) > 0
131 	    && strlen(session->sasl_mechanism_list) < var_line_limit) {
132 	    junk = concatenate(session->sasl_mechanism_list, " ", mech_list,
133 			       (char *) 0);
134 	    myfree(session->sasl_mechanism_list);
135 	    session->sasl_mechanism_list = junk;
136 	}
137 	return;
138     }
139     if (strlen(mech_list) > 0) {
140 	session->sasl_mechanism_list = mystrdup(mech_list);
141     } else {
142 	msg_warn(*words ? "%s offered no supported AUTH mechanisms: '%s'" :
143 		 "%s offered null AUTH mechanism list%s",
144 		 session->namaddrport, words);
145     }
146     session->features |= SMTP_FEATURE_AUTH;
147 }
148 
149 /* smtp_sasl_helo_login - perform SASL login */
150 
151 int     smtp_sasl_helo_login(SMTP_STATE *state)
152 {
153     SMTP_SESSION *session = state->session;
154     DSN_BUF *why = state->why;
155     int     ret;
156 
157     /*
158      * Skip authentication when no authentication info exists for this
159      * server, so that we talk to each other like strangers.
160      */
161     if (smtp_sasl_passwd_lookup(session) == 0) {
162 	session->features &= ~SMTP_FEATURE_AUTH;
163 	return 0;
164     }
165 
166     /*
167      * Otherwise, if authentication information exists, assume that
168      * authentication is required, and assume that an authentication error is
169      * recoverable from the message delivery point of view. An authentication
170      * error is unrecoverable from a session point of view - the session will
171      * not be reused.
172      */
173     ret = 0;
174     if (session->sasl_mechanism_list == 0) {
175 	dsb_simple(why, "4.7.0", "SASL authentication failed: "
176 		   "server %s offered no compatible authentication mechanisms for this type of connection security",
177 		   session->namaddr);
178 	ret = smtp_sess_fail(state);
179 	/* Session reuse is disabled. */
180     } else {
181 #ifndef USE_TLS
182 	smtp_sasl_start(session, VAR_LMTP_SMTP(SASL_OPTS), var_smtp_sasl_opts);
183 #else
184 	if (session->tls_context == 0)
185 	    smtp_sasl_start(session, VAR_LMTP_SMTP(SASL_OPTS),
186 			    var_smtp_sasl_opts);
187 	else if (TLS_CERT_IS_MATCHED(session->tls_context))
188 	    smtp_sasl_start(session, VAR_LMTP_SMTP(SASL_TLSV_OPTS),
189 			    var_smtp_sasl_tlsv_opts);
190 	else
191 	    smtp_sasl_start(session, VAR_LMTP_SMTP(SASL_TLS_OPTS),
192 			    var_smtp_sasl_tls_opts);
193 #endif
194 	if (smtp_sasl_authenticate(session, why) <= 0) {
195 	    ret = smtp_sess_fail(state);
196 	    /* Session reuse is disabled. */
197 	}
198     }
199     return (ret);
200 }
201 
202 #endif
203