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