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