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>/ */
89typedef struct {
90	XSASL_CLIENT_IMPL xsasl;	/* generic members, must be first */
91	saslc_t *saslc;			/* saslc context */
92} XSASL_SASLC_CLIENT_IMPL;
93
94typedef 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
103static XSASL_CLIENT *xsasl_saslc_client_create(XSASL_CLIENT_IMPL *,
104    XSASL_CLIENT_CREATE_ARGS *);
105static int xsasl_saslc_client_first(XSASL_CLIENT *, const char *,
106    const char *, const char *, const char **, VSTRING *);
107static int xsasl_saslc_client_next(XSASL_CLIENT *, const char *,
108    VSTRING *);
109static void xsasl_saslc_client_done(XSASL_CLIENT_IMPL *);
110static void xsasl_saslc_client_free(XSASL_CLIENT *);
111
112static void
113setprop(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 */
124static int
125xsasl_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 */
186static int
187xsasl_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 */
222void
223xsasl_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 */
236XSASL_CLIENT *
237xsasl_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 */
271static void
272xsasl_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 */
287XSASL_CLIENT_IMPL *
288xsasl_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