1 /* $Id: mech_gssapi.c,v 1.2 2011/01/29 23:35:31 agc Exp $ */ 2 3 /* Copyright (c) 2010 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Mateusz Kocielski. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the NetBSD 20 * Foundation, Inc. and its contributors. 21 * 4. Neither the name of The NetBSD Foundation nor the names of its 22 * contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <stdio.h> 39 #include <string.h> 40 #include <gssapi/gssapi.h> 41 #include <assert.h> 42 #include <saslc.h> 43 #include "saslc_private.h" 44 #include "mech.h" 45 46 /* RFC2222 implementation 47 * see: RFC2222 7.2.1 section 48 */ 49 50 /* local headers */ 51 52 /* properties */ 53 #define SASLC_GSSAPI_AUTHID "AUTHID" 54 #define SASLC_GSSAPI_HOSTNAME "HOSTNAME" 55 #define SASLC_GSSAPI_SERVICE "SERVICE" 56 57 /* status */ 58 enum { 59 GSSAPI_INIT, /* initialization see: RFC2222 7.2.1 section */ 60 GSSAPI_UNWRAP, /* unwrap see: RFC2222 7.2.1 section */ 61 GSSAPI_AUTHENTICATED /* authenticated see: RFC2222 7.2.3 section */ 62 }; 63 64 typedef enum { 65 LAYER_NONE = 1, /* no security layer */ 66 LAYER_INT = 2, /* integrity */ 67 LAYER_CONF = 4 /* privacy */ 68 } saslc__mech_gssapi_sl_t; 69 70 /** gssapi mechanism session */ 71 typedef struct { 72 saslc__mech_sess_t mech_sess; /**< mechanism session */ 73 /* additional stuff */ 74 int status; /**< authentication status */ 75 gss_name_t name; /**< service\@hostname */ 76 gss_ctx_id_t context; /**< GSSAPI context */ 77 saslc__mech_gssapi_sl_t layer; /**< security layer */ 78 } saslc__mech_gssapi_sess_t; 79 80 static int saslc__mech_gssapi_create(saslc_sess_t *); 81 static int saslc__mech_gssapi_destroy(saslc_sess_t *); 82 static int saslc__mech_gssapi_encode(saslc_sess_t *, const void *, size_t, 83 void **, size_t *); 84 static int saslc__mech_gssapi_decode(saslc_sess_t *, const void *, size_t, 85 void **, size_t *); 86 static int saslc__mech_gssapi_cont(saslc_sess_t *, const void *, size_t, 87 void **, size_t *); 88 89 /** 90 * @brief creates gssapi mechanism session. 91 * Function initializes also default options for the session. 92 * @param sess sasl session 93 * @return 0 on success, -1 on failure. 94 */ 95 96 static int 97 saslc__mech_gssapi_create(saslc_sess_t *sess) 98 { 99 saslc__mech_gssapi_sess_t *c; 100 101 c = sess->mech_sess = calloc(1, sizeof(*c)); 102 if (c == NULL) 103 return -1; 104 105 sess->mech_sess = c; 106 107 /* GSSAPI init stuff */ 108 c->context = GSS_C_NO_CONTEXT; 109 c->name = GSS_C_NO_NAME; 110 c->layer = LAYER_NONE; 111 112 return 0; 113 } 114 115 /** 116 * @brief destroys gssapi mechanism session. 117 * Function also is freeing assigned resources to the session. 118 * @param sess sasl session 119 * @return Functions always returns 0. 120 */ 121 122 static int 123 saslc__mech_gssapi_destroy(saslc_sess_t *sess) 124 { 125 saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess; 126 OM_uint32 min_s; 127 128 (void)gss_release_name(&min_s, &mech_sess->name); 129 130 free(sess->mech_sess); 131 sess->mech_sess = NULL; 132 133 return 0; 134 } 135 136 137 /** 138 * @brief encodes data using negotiated security layer 139 * @param sess sasl session 140 * @param in input data 141 * @param inlen input data length 142 * @param out place to store output data 143 * @param outlen output data length 144 * @return MECH_OK - success, 145 * MECH_ERROR - error 146 */ 147 148 static int 149 saslc__mech_gssapi_encode(saslc_sess_t *sess, const void *in, size_t inlen, 150 void **out, size_t *outlen) 151 { 152 saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess; 153 gss_buffer_desc input, output; 154 OM_uint32 min_s, maj_s; 155 156 if (mech_sess->status != GSSAPI_AUTHENTICATED) 157 return MECH_ERROR; 158 159 /* No layer was negotiated - just copy data */ 160 if (mech_sess->layer == LAYER_NONE) { 161 *outlen = inlen; 162 *out = calloc(*outlen, sizeof(char)); 163 memcpy(*out, in, *outlen); 164 return MECH_OK; 165 } 166 167 input.value = (char *)(intptr_t)in; 168 input.length = inlen; 169 170 maj_s = gss_wrap(&min_s, mech_sess->context, 171 mech_sess->layer, GSS_C_QOP_DEFAULT, &input, NULL, 172 &output); 173 174 if (GSS_ERROR(maj_s)) 175 return MECH_ERROR; 176 177 *outlen = output.length; 178 if (*outlen > 0) { 179 *out = calloc(*outlen, sizeof(char)); 180 if (*out == NULL) { 181 saslc__error_set_errno(ERR(sess), 182 ERROR_NOMEM); 183 return MECH_ERROR; 184 } 185 memcpy(*out, output.value, *outlen); 186 (void)gss_release_buffer(&min_s, &output); 187 } else 188 *out = NULL; 189 190 return MECH_OK; 191 } 192 193 /** 194 * @brief decodes data using negotiated security layer 195 * @param sess sasl session 196 * @param in input data 197 * @param inlen input data length 198 * @param out place to store output data 199 * @param outlen output data length 200 * @return MECH_OK - success, 201 * MECH_ERROR - error 202 */ 203 204 static int 205 saslc__mech_gssapi_decode(saslc_sess_t *sess, const void *in, size_t inlen, 206 void **out, size_t *outlen) 207 { 208 saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess; 209 gss_buffer_desc input, output; 210 OM_uint32 min_s, maj_s; 211 212 if (mech_sess->status != GSSAPI_AUTHENTICATED) 213 return MECH_ERROR; 214 215 /* No layer was negotiated - just copy data */ 216 if (mech_sess->layer == LAYER_NONE) { 217 *outlen = inlen; 218 *out = calloc(*outlen, sizeof(char)); 219 memcpy(*out, in, *outlen); 220 return MECH_OK; 221 } 222 223 input.value = (char *)(intptr_t)in; 224 input.length = inlen; 225 226 maj_s = gss_unwrap(&min_s, mech_sess->context, &input, &output, NULL, 227 NULL); 228 229 if (GSS_ERROR(maj_s)) 230 return MECH_ERROR; 231 232 *outlen = output.length; 233 if (*outlen > 0) { 234 *out = calloc(*outlen, sizeof(char)); 235 if (*out == NULL) { 236 saslc__error_set_errno(ERR(sess), 237 ERROR_NOMEM); 238 return MECH_ERROR; 239 } 240 memcpy(*out, output.value, *outlen); 241 (void)gss_release_buffer(&min_s, &output); 242 } else 243 *out = NULL; 244 245 return MECH_OK; 246 } 247 248 /** 249 * @brief doing one step of the sasl authentication 250 * 251 * @param sess sasl session 252 * @param in input data 253 * @param inlen input data length 254 * @param out place to store output data 255 * @param outlen output data length 256 * @return MECH_OK - success, 257 * MECH_STEP - more steps are needed, 258 * MECH_ERROR - error 259 */ 260 261 /*ARGSUSED*/ 262 static int 263 saslc__mech_gssapi_cont(saslc_sess_t *sess, const void *in, size_t inlen, 264 void **out, size_t *outlen) 265 { 266 gss_buffer_t input_buf; 267 gss_buffer_desc input, output, name; 268 const char *hostname, *service, *authid; 269 char *input_name; 270 saslc__mech_gssapi_sess_t *mech_sess = sess->mech_sess; 271 OM_uint32 min_s, maj_s; 272 int len; 273 274 switch(mech_sess->status) { 275 case GSSAPI_INIT: 276 /* setup name at the very begining */ 277 if (mech_sess->name == GSS_C_NO_NAME) { 278 /* gss_import_name */ 279 if ((hostname = saslc_sess_getprop(sess, 280 SASLC_GSSAPI_HOSTNAME)) == NULL) { 281 saslc__error_set(ERR(sess), ERROR_MECH, 282 "hostname is required for an " 283 "authentication"); 284 return MECH_ERROR; 285 } 286 287 if ((service = saslc_sess_getprop(sess, 288 SASLC_GSSAPI_SERVICE)) == NULL) { 289 saslc__error_set(ERR(sess), ERROR_MECH, 290 "service is required for an " 291 "authentication"); 292 return MECH_ERROR; 293 } 294 295 len = asprintf(&input_name, "%s@%s", service, hostname); 296 if (len == -1) { 297 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 298 return MECH_ERROR; 299 } 300 name.value = input_name; 301 name.length = (size_t)len + 1; 302 303 maj_s = gss_import_name(&min_s, &name, 304 GSS_C_NT_HOSTBASED_SERVICE, &mech_sess->name); 305 306 free(input_name); 307 308 if (GSS_ERROR(maj_s)) 309 return MECH_ERROR; 310 311 } 312 313 /* input data is passed */ 314 if (in != NULL && inlen > 0) { 315 input.value = (char *)(intptr_t)in; 316 input.length = inlen; 317 input_buf = &input; 318 } else { 319 input_buf = GSS_C_NO_BUFFER; 320 } 321 322 maj_s = gss_init_sec_context(&min_s, GSS_C_NO_CREDENTIAL, 323 &mech_sess->context, mech_sess->name, GSS_C_NO_OID, 324 (OM_uint32)(GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG), 0, 325 GSS_C_NO_CHANNEL_BINDINGS, input_buf, NULL, &output, NULL, 326 NULL); 327 328 switch(maj_s) { 329 case GSS_S_COMPLETE: 330 /* init complete, now do unwrap */ 331 mech_sess->status = GSSAPI_UNWRAP; 332 /*FALLTHROUGH*/ 333 case GSS_S_CONTINUE_NEEDED: 334 *outlen = output.length; 335 if (*outlen > 0) { 336 *out = calloc(*outlen, sizeof(char)); 337 if (*out == NULL) { 338 saslc__error_set_errno(ERR(sess), 339 ERROR_NOMEM); 340 return MECH_ERROR; 341 } 342 memcpy(*out, output.value, *outlen); 343 (void)gss_release_buffer(&min_s, &output); 344 } else 345 *out = NULL; 346 return MECH_STEP; 347 default: 348 /* error occured */ 349 return MECH_ERROR; 350 } 351 352 case GSSAPI_UNWRAP: 353 input.value = (char *)(intptr_t)in; 354 input.length = inlen; 355 356 maj_s = gss_unwrap(&min_s, mech_sess->context, &input, 357 &output, NULL, NULL); 358 359 if ((authid = saslc_sess_getprop(sess, SASLC_GSSAPI_AUTHID)) 360 == NULL) { 361 saslc__error_set(ERR(sess), ERROR_MECH, 362 "authid is required for an authentication"); 363 return MECH_ERROR; 364 } 365 366 len = asprintf(&input_name, "%c%c%c%c%s", 0, 0, 0, 0, authid); 367 if (len == -1) { 368 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 369 return MECH_ERROR; 370 } 371 name.value = input_name; 372 input.length = (size_t)len + 1; 373 (void)gss_release_buffer(&min_s, &output); 374 375 maj_s = gss_wrap(&min_s, mech_sess->context, 376 0 /* FALSE - RFC2222 */, GSS_C_QOP_DEFAULT, &input, NULL, 377 &output); 378 379 free(input.value); 380 381 if (GSS_ERROR(maj_s)) 382 return MECH_ERROR; 383 384 *outlen = output.length; 385 if (*outlen > 0) { 386 *out = calloc(*outlen, sizeof(char)); 387 if (*out == NULL) { 388 saslc__error_set_errno(ERR(sess), 389 ERROR_NOMEM); 390 return MECH_ERROR; 391 } 392 memcpy(*out, output.value, *outlen); 393 (void)gss_release_buffer(&min_s, &output); 394 } else 395 *out = NULL; 396 397 mech_sess->status = GSSAPI_AUTHENTICATED; 398 return MECH_OK; 399 default: 400 assert(/*CONSTCOND*/0); /* impossible */ 401 /*NOTREACHED*/ 402 return MECH_ERROR; 403 } 404 } 405 406 /* mechanism definition */ 407 const saslc__mech_t saslc__mech_gssapi = { 408 "GSSAPI", /* name */ 409 saslc__mech_gssapi_create, /* create */ 410 saslc__mech_gssapi_cont, /* step */ 411 saslc__mech_gssapi_encode, /* encode */ 412 saslc__mech_gssapi_decode, /* decode */ 413 saslc__mech_gssapi_destroy /* destroy */ 414 }; 415