xref: /netbsd-src/external/ibm-public/postfix/dist/src/xsasl/xsasl_saslc_client.c (revision 4f7d5d1727ce040bc0c4eaace124e53655db413e)
1 /*	$NetBSD: xsasl_saslc_client.c,v 1.2 2021/02/05 21:45:24 joerg Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	xsasl_saslc_client 3
6 /* SUMMARY
7 /*	saslc SASL client-side plug-in
8 /* SYNOPSIS
9 /*	#include <xsasl_saslc_client.h>
10 /*
11 /*	XSASL_CLIENT_IMPL *xsasl_saslc_client_init(client_type, path_info)
12 /*	const char *client_type;
13 /* DESCRIPTION
14 /*	This module implements the saslc SASL client-side authentication
15 /*	plug-in.
16 /*
17 /*	xsasl_saslc_client_init() initializes the saslc SASL library and
18 /*	returns an implementation handle that can be used to generate
19 /*	SASL client instances.
20 /*
21 /*	Arguments:
22 /* .IP client_type
23 /*	The plug-in SASL client type (saslc). This argument is
24 /*	ignored, but it could be used when one implementation
25 /*	provides multiple variants.
26 /* .IP path_info
27 /*	Implementation-specific information to specify the location
28 /*	of a configuration file, rendez-vous point, etc. This
29 /*	information is ignored by the saslc SASL client plug-in.
30 /* DIAGNOSTICS
31 /*	Fatal: out of memory.
32 /*
33 /*	Panic: interface violation.
34 /*
35 /*	Other: the routines log a warning and return an error result
36 /*	as specified in xsasl_client(3).
37 /* SEE ALSO
38 /*	xsasl_client(3) Client API
39 /* LICENSE
40 /* .ad
41 /* .fi
42 /*	The Secure Mailer license must be distributed with this software.
43 /* AUTHOR(S)
44 /*	Original author:
45 /*	Till Franke
46 /*	SuSE Rhein/Main AG
47 /*	65760 Eschborn, Germany
48 /*
49 /*	Adopted by:
50 /*	Wietse Venema
51 /*	IBM T.J. Watson Research
52 /*	P.O. Box 704
53 /*	Yorktown Heights, NY 10598, USA
54 /*--*/
55 
56 #if defined(USE_SASL_AUTH) && defined(USE_SASLC_SASL)
57 
58  /*
59   * System headers.
60   */
61 #include <errno.h>
62 #include <saslc.h>
63 #include <stdlib.h>
64 #include <string.h>
65 
66 #include "sys_defs.h"
67 
68  /*
69   * Utility library
70   */
71 #include "msg.h"
72 #include "mymalloc.h"
73 #include "stringops.h"
74 
75  /*
76   * Global library
77   */
78 #include "mail_params.h"
79 
80  /*
81   * Application-specific
82   */
83 #include "xsasl.h"
84 #include "xsasl_saslc.h"
85 
86 
87 #define XSASL_SASLC_APPNAME	"postfix"  /* The config files are in
88 					      /etc/saslc.d/<appname>/ */
89 typedef struct {
90 	XSASL_CLIENT_IMPL xsasl;	/* generic members, must be first */
91 	saslc_t *saslc;			/* saslc context */
92 } XSASL_SASLC_CLIENT_IMPL;
93 
94 typedef struct {
95 	XSASL_CLIENT xsasl;		/* generic members, must be first */
96 	saslc_t *saslc;			/* saslc context */
97 	saslc_sess_t *sess;		/* session context */
98 	const char *service;		/* service (smtp) */
99 	const char *hostname;		/* server host name */
100 	const char *sec_opts;		/* security options */
101 } XSASL_SASLC_CLIENT;
102 
103 static XSASL_CLIENT *xsasl_saslc_client_create(XSASL_CLIENT_IMPL *,
104     XSASL_CLIENT_CREATE_ARGS *);
105 static int xsasl_saslc_client_first(XSASL_CLIENT *, const char *,
106     const char *, const char *, const char **, VSTRING *);
107 static int xsasl_saslc_client_next(XSASL_CLIENT *, const char *,
108     VSTRING *);
109 static void xsasl_saslc_client_done(XSASL_CLIENT_IMPL *);
110 static void xsasl_saslc_client_free(XSASL_CLIENT *);
111 
112 static void
setprop(saslc_sess_t * sess,int overwrite,const char * key,const char * value)113 setprop(saslc_sess_t *sess, int overwrite, const char *key, const char *value)
114 {
115 
116 	if (overwrite != 0 ||
117 	    saslc_sess_getprop(sess, key) == NULL)
118 		saslc_sess_setprop(sess, key, value);
119 }
120 
121 /*
122  * Run authentication protocol: first step.
123  */
124 static int
xsasl_saslc_client_first(XSASL_CLIENT * xp,const char * mechanism_list,const char * username,const char * password,const char ** mechanism,VSTRING * init_resp)125 xsasl_saslc_client_first(
126 	XSASL_CLIENT *xp,
127 	const char *mechanism_list,
128 	const char *username,
129 	const char *password,
130 	const char **mechanism,
131 	VSTRING *init_resp)
132 {
133 	XSASL_SASLC_CLIENT *client = (XSASL_SASLC_CLIENT *)xp;
134 	const char *mech;
135 	void *out;
136 	size_t outlen;
137 	int rv;
138 
139 	if (msg_verbose) {
140 		msg_info("%s: mechanism_list='%s'", __func__, mechanism_list);
141 		msg_info("%s: username='%s'", __func__, username);
142 /*		msg_info("%s: password='%s'", __func__, password); */
143 	}
144 	client->sess = saslc_sess_init(client->saslc, mechanism_list,
145 					client->sec_opts);
146 	if (client->sess == NULL) {
147 		msg_info("%s: saslc_sess_init failed", __func__);
148 		return XSASL_AUTH_FAIL;
149 	}
150 	mech = saslc_sess_getmech(client->sess);
151 	if (mechanism)
152 		*mechanism = mech;
153 	if (msg_verbose)
154 		msg_info("%s: mechanism='%s'", __func__, mech);
155 
156 	setprop(client->sess, 0, SASLC_PROP_AUTHCID,  username);
157 	setprop(client->sess, 1, SASLC_PROP_PASSWD,   password);
158 	setprop(client->sess, 1, SASLC_PROP_SERVICE,  client->service);
159 	setprop(client->sess, 1, SASLC_PROP_HOSTNAME, client->hostname);
160 	setprop(client->sess, 1, SASLC_PROP_BASE64IO, "true");
161 	setprop(client->sess, 0, SASLC_PROP_QOPMASK,  "auth");
162 
163 	if ((rv = saslc_sess_cont(client->sess, NULL, 0, &out, &outlen))
164 	    == -1) {
165 		msg_info("%s: saslc_sess_encode='%s'", __func__,
166 		    saslc_sess_strerror(client->sess));
167 		return XSASL_AUTH_FAIL;
168 	}
169 	vstring_strcpy(init_resp, outlen ? out : "");
170 	if (msg_verbose) {
171 		msg_info("%s: client_reply='%s'", __func__,
172 		    outlen ? (const char *)out : "");
173 	}
174 
175 	if (outlen > 0)
176 		memset(out, 0, outlen);		/* XXX: silly? */
177 	if (out != NULL)
178 		free (out);
179 
180 	return XSASL_AUTH_OK;
181 }
182 
183 /*
184  * Continue authentication.
185  */
186 static int
xsasl_saslc_client_next(XSASL_CLIENT * xp,const char * server_reply,VSTRING * client_reply)187 xsasl_saslc_client_next(XSASL_CLIENT *xp, const char *server_reply,
188     VSTRING *client_reply)
189 {
190 	XSASL_SASLC_CLIENT *client;
191 	void *out;
192 	size_t outlen;
193 
194 	client = (XSASL_SASLC_CLIENT *)xp;
195 
196 	if (msg_verbose)
197 		msg_info("%s: server_reply='%s'", __func__, server_reply);
198 
199 	if (saslc_sess_cont(client->sess, server_reply, strlen(server_reply),
200 	    &out, &outlen) == -1) {
201 		msg_info("%s: saslc_sess_encode='%s'", __func__,
202 		    saslc_sess_strerror(client->sess));
203 		return XSASL_AUTH_FAIL;
204 	}
205 	vstring_strcpy(client_reply, outlen ? out : "");
206 	if (msg_verbose) {
207 		msg_info("%s: client_reply='%s'", __func__,
208 		    outlen ? (const char *) out : "");
209 	}
210 
211 	if (outlen > 0)
212 		memset(out, 0, outlen);		/* XXX: silly? */
213 	if (out != NULL)
214 		free (out);
215 
216 	return XSASL_AUTH_OK;
217 }
218 
219 /*
220  * Per-session cleanup.
221  */
222 void
xsasl_saslc_client_free(XSASL_CLIENT * xp)223 xsasl_saslc_client_free(XSASL_CLIENT *xp)
224 {
225 	XSASL_SASLC_CLIENT *client;
226 
227 	client = (XSASL_SASLC_CLIENT *)xp;
228 	if (client->sess)
229 		saslc_sess_end(client->sess);
230 	myfree((char *)client);
231 }
232 
233 /*
234  * Per-session SASL initialization.
235  */
236 XSASL_CLIENT *
xsasl_saslc_client_create(XSASL_CLIENT_IMPL * impl,XSASL_CLIENT_CREATE_ARGS * args)237 xsasl_saslc_client_create(XSASL_CLIENT_IMPL *impl,
238     XSASL_CLIENT_CREATE_ARGS *args)
239 {
240 	XSASL_SASLC_CLIENT_IMPL *xp;
241 	XSASL_SASLC_CLIENT *client;
242 
243 	xp = (XSASL_SASLC_CLIENT_IMPL *)impl;
244 	if (msg_verbose) {
245 		msg_info("%s: service='%s'", __func__, args->service);
246 		msg_info("%s: server_name='%s'", __func__, args->server_name);
247 		msg_info("%s: security_options='%s'", __func__,
248 		    args->security_options);
249 	}
250 
251 	/* NB: mymalloc never returns NULL, it calls _exit(3) instead */
252 	client = (XSASL_SASLC_CLIENT *)mymalloc(sizeof(*client));
253 
254 	client->xsasl.free  = xsasl_saslc_client_free;
255 	client->xsasl.first = xsasl_saslc_client_first;
256 	client->xsasl.next  = xsasl_saslc_client_next;
257 
258 	client->saslc = xp->saslc;
259 
260 	/* XXX: should these be strdup()ed? */
261 	client->service  = args->service;
262 	client->hostname = args->server_name;
263 	client->sec_opts = args->security_options;
264 
265 	return &client->xsasl;
266 }
267 
268 /*
269  * Dispose of implementation.
270  */
271 static void
xsasl_saslc_client_done(XSASL_CLIENT_IMPL * impl)272 xsasl_saslc_client_done(XSASL_CLIENT_IMPL *impl)
273 {
274 	XSASL_SASLC_CLIENT_IMPL *xp;
275 
276 	xp = (XSASL_SASLC_CLIENT_IMPL *)impl;
277 	if (xp->saslc) {
278 		saslc_end(xp->saslc);
279 		xp->saslc = NULL;  /* XXX: unnecessary as freeing impl */
280 	}
281 	myfree((char *)impl);
282 }
283 
284 /*
285  * Initialize saslc SASL library.
286  */
287 XSASL_CLIENT_IMPL *
xsasl_saslc_client_init(const char * client_type,const char * path_info)288 xsasl_saslc_client_init(const char *client_type, const char *path_info)
289 {
290 	XSASL_SASLC_CLIENT_IMPL *xp;
291 
292 	/* XXX: This should be unnecessary! */
293 	if (strcmp(client_type, XSASL_TYPE_SASLC) != 0) {
294 		msg_info("%s: invalid client_type: '%s'", __func__,
295 		    client_type);
296 		return NULL;
297 	}
298 	if (msg_verbose) {
299 		msg_info("%s: client_type='%s'", __func__, client_type);
300 		msg_info("%s: path_info='%s'",   __func__, path_info);
301 	}
302 
303 	/* NB: mymalloc() never returns NULL, it calls _exit(3) instead */
304 	xp = (XSASL_SASLC_CLIENT_IMPL *)mymalloc(sizeof(*xp));
305 	xp->xsasl.create = xsasl_saslc_client_create;
306 	xp->xsasl.done = xsasl_saslc_client_done;
307 
308 	/* NB: msg_fatal() exits the program immediately after printing */
309 	if ((xp->saslc = saslc_alloc()) == NULL)
310 		msg_fatal("%s: saslc_alloc failed: %s", __func__,
311 		    strerror(errno));
312 
313 	if (saslc_init(xp->saslc, XSASL_SASLC_APPNAME, path_info) == -1)
314 		msg_fatal("%s: saslc_init failed: %s", __func__,
315 		    saslc_strerror(xp->saslc));
316 
317 	return &xp->xsasl;
318 }
319 
320 #endif /* defined(USE_SASL_AUTH) && defined(USE_SASLC_SASL) */
321