1 /* $NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric 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 #include <sys/cdefs.h>
38 __RCSID("$NetBSD: mech_gssapi.c,v 1.7 2013/05/16 13:02:12 elric Exp $");
39
40 #include <assert.h>
41 #include <errno.h>
42 #include <limits.h> /* for LINE_MAX */
43 #include <saslc.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include <gssapi/gssapi.h>
49
50 #include "buffer.h"
51 #include "list.h"
52 #include "mech.h"
53 #include "msg.h"
54 #include "saslc_private.h"
55
56 /* See RFC 2222 section 7.2.1. */
57
58 /* properties */
59 #define SASLC_GSSAPI_AUTHCID SASLC_PROP_AUTHCID
60 #define SASLC_GSSAPI_HOSTNAME SASLC_PROP_HOSTNAME
61 #define SASLC_GSSAPI_SERVICE SASLC_PROP_SERVICE
62 #define SASLC_GSSAPI_QOPMASK SASLC_PROP_QOPMASK
63
64 #define DEFAULT_QOP_MASK (F_QOP_NONE | F_QOP_INT | F_QOP_CONF)
65
66 /* authentication steps */
67 typedef enum { /* see RFC2222 7.2.1 section */
68 GSSAPI_AUTH_FIRST, /* first authentication stage */
69 GSSAPI_AUTH_NEXT, /* next authentication stage(s) */
70 GSSAPI_AUTH_LAST, /* final authentication stage */
71 GSSAPI_AUTH_DONE /* authenticated */
72 } saslc__mech_gssapi_status_t;
73
74 /* gssapi mechanism session */
75 typedef struct {
76 saslc__mech_sess_t mech_sess; /* mechanism session */
77 saslc__mech_gssapi_status_t status; /* authentication status */
78 gss_ctx_id_t gss_ctx; /* GSSAPI context */
79 gss_name_t server_name; /* server name: service@host */
80 gss_name_t client_name; /* client name - XXX: unused! */
81 uint32_t qop_mask; /* available QOP services */
82 uint32_t omaxbuf; /* maximum output buffer size */
83 uint32_t imaxbuf; /* maximum input buffer size */
84 saslc__buffer32_context_t *dec_ctx; /* decode buffer context */
85 saslc__buffer_context_t *enc_ctx; /* encode buffer context */
86 } saslc__mech_gssapi_sess_t;
87
88 /**
89 * @brief creates gssapi mechanism session.
90 * Function initializes also default options for the session.
91 * @param sess sasl session
92 * @return 0 on success, -1 on failure.
93 */
94 static int
saslc__mech_gssapi_create(saslc_sess_t * sess)95 saslc__mech_gssapi_create(saslc_sess_t *sess)
96 {
97 saslc__mech_gssapi_sess_t *c;
98
99 c = sess->mech_sess = calloc(1, sizeof(*c));
100 if (c == NULL)
101 return -1;
102
103 sess->mech_sess = c;
104
105 c->gss_ctx = GSS_C_NO_CONTEXT;
106 c->server_name = GSS_C_NO_NAME;
107 c->client_name = GSS_C_NO_NAME;
108
109 return 0;
110 }
111
112 /**
113 * @brief destroys gssapi mechanism session.
114 * Function also is freeing assigned resources to the session.
115 * @param sess sasl session
116 * @return Functions always returns 0.
117 */
118 static int
saslc__mech_gssapi_destroy(saslc_sess_t * sess)119 saslc__mech_gssapi_destroy(saslc_sess_t *sess)
120 {
121 saslc__mech_gssapi_sess_t *ms;
122 OM_uint32 min_s;
123
124 ms = sess->mech_sess;
125
126 if (ms->gss_ctx != GSS_C_NO_CONTEXT)
127 gss_delete_sec_context(&min_s, &ms->gss_ctx, GSS_C_NO_BUFFER);
128 if (ms->server_name != GSS_C_NO_NAME)
129 gss_release_name(&min_s, &ms->server_name);
130 if (ms->client_name != GSS_C_NO_NAME)
131 gss_release_name(&min_s, &ms->client_name);
132
133 saslc__buffer_destroy(ms->enc_ctx);
134 saslc__buffer32_destroy(ms->dec_ctx);
135 free(ms);
136 sess->mech_sess = NULL;
137
138 return 0;
139 }
140
141 /**
142 * @brief translate the major and minor statuses an error message for
143 * the given mechanism
144 * @param maj_s major status
145 * @param min_s minor status
146 * @param mech mechanism
147 * @return pointer to a static buffer with error message
148 */
149 static char *
saslc__mech_gssapi_err(OM_uint32 maj_s,OM_uint32 min_s,gss_OID mech)150 saslc__mech_gssapi_err(OM_uint32 maj_s, OM_uint32 min_s, gss_OID mech)
151 {
152 static char errbuf[LINE_MAX];
153 gss_buffer_desc maj_error_message;
154 gss_buffer_desc min_error_message;
155 OM_uint32 disp_min_s;
156 OM_uint32 msg_ctx;
157
158 msg_ctx = 0;
159 maj_error_message.length = 0;
160 maj_error_message.value = NULL;
161 min_error_message.length = 0;
162 min_error_message.value = NULL;
163
164 (void)gss_display_status(&disp_min_s, maj_s, GSS_C_GSS_CODE,
165 mech, &msg_ctx, &maj_error_message);
166 (void)gss_display_status(&disp_min_s, min_s, GSS_C_MECH_CODE,
167 mech, &msg_ctx, &min_error_message);
168
169 (void)snprintf(errbuf, sizeof(errbuf),
170 "gss-code: %lu %.*s\nmech-code: %lu %.*s",
171 (unsigned long)maj_s,
172 (int)maj_error_message.length,
173 (char *)maj_error_message.value,
174 (unsigned long)min_s,
175 (int)min_error_message.length,
176 (char *)min_error_message.value);
177
178 (void)gss_release_buffer(&disp_min_s, &maj_error_message);
179 (void)gss_release_buffer(&disp_min_s, &min_error_message);
180
181 return errbuf;
182 }
183
184 /**
185 * @brief set a session error message using saslc__mech_gssapi_err()
186 * @param sess the session
187 * @param err error number to set
188 * @param maj_s major status
189 * @param min_s minor status
190 * @return pointer to a static buffer with error message
191 */
192 static void
saslc__mech_gssapi_set_err(saslc_sess_t * sess,int err,OM_uint32 maj_s,OM_uint32 min_s)193 saslc__mech_gssapi_set_err(saslc_sess_t *sess, int err, OM_uint32 maj_s, OM_uint32 min_s)
194 {
195
196 saslc__error_set(ERR(sess), err,
197 saslc__mech_gssapi_err(maj_s, min_s, GSS_C_NO_OID));
198 }
199
200 /**
201 * @brief convert an initialization output token into the out and outlen format.
202 * Also releases the output token.
203 * @param sess saslc session
204 * @param outbuf gss buffer token
205 * @param out pointer to a void pointer
206 * @param outlen pointer to size_t length storage
207 * @returns 0 on success, -1 on failure
208 */
209 static int
prep_output(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)210 prep_output(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
211 {
212 OM_uint32 min_s;
213
214 if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
215 *outlen = 0;
216 *out = NULL;
217 return 0;
218 }
219 if (outbuf->length == 0) {
220 *outlen = 0;
221 *out = NULL;
222 gss_release_buffer(&min_s, outbuf);
223 return 0;
224 }
225 *out = malloc(outbuf->length);
226 if (*out == NULL) {
227 *outlen = 0;
228 gss_release_buffer(&min_s, outbuf);
229 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
230 return -1;
231 }
232 *outlen = outbuf->length;
233 memcpy(*out, outbuf->value, outbuf->length);
234 gss_release_buffer(&min_s, outbuf);
235 return 0;
236 }
237
238 /**
239 * @brief convert an output token into a valid packet where the first
240 * 4 bytes are the payload length in network byte order.
241 * Also releases the output token.
242 * @param sess saslc session
243 * @param outbuf gss buffer token
244 * @param out pointer to a void pointer
245 * @param outlen pointer to size_t length storage
246 * @returns 0 on success, -1 on failure
247 */
248 static int
prep_packet(saslc_sess_t * sess,gss_buffer_t outbuf,void ** out,size_t * outlen)249 prep_packet(saslc_sess_t *sess, gss_buffer_t outbuf, void **out, size_t *outlen)
250 {
251 saslc__mech_gssapi_sess_t *ms;
252 OM_uint32 min_s;
253 char *buf;
254 size_t buflen;
255
256 ms = sess->mech_sess;
257
258 if (outbuf == GSS_C_NO_BUFFER || outbuf->value == NULL) {
259 *outlen = 0;
260 *out = NULL;
261 return 0;
262 }
263 if (outbuf->length == 0) {
264 *outlen = 0;
265 *out = NULL;
266 gss_release_buffer(&min_s, outbuf);
267 return 0;
268 }
269 buflen = outbuf->length + 4;
270 if (buflen > ms->omaxbuf) {
271 saslc__error_set(ERR(sess), ERROR_MECH,
272 "output exceeds server maxbuf size");
273 gss_release_buffer(&min_s, outbuf);
274 return -1;
275 }
276 buf = malloc(buflen);
277 if (buf == NULL) {
278 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
279 return -1;
280 }
281 be32enc(buf, (uint32_t)outbuf->length);
282 memcpy(buf + 4, outbuf->value, outbuf->length);
283 gss_release_buffer(&min_s, outbuf);
284
285 *out = buf;
286 *outlen = buflen;
287 return 0;
288 }
289
290 /**
291 * @brief encodes one block of data using the negotiated security layer.
292 * @param sess sasl session
293 * @param in input data
294 * @param inlen input data length
295 * @param out place to store output data
296 * @param outlen output data length
297 * @return number of bytes consumed, zero if more needed, or -1 on failure.
298 */
299 static ssize_t
saslc__mech_gssapi_encode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)300 saslc__mech_gssapi_encode(saslc_sess_t *sess, const void *in, size_t inlen,
301 void **out, size_t *outlen)
302 {
303 saslc__mech_gssapi_sess_t *ms;
304 gss_buffer_desc input, output;
305 OM_uint32 min_s, maj_s;
306 uint8_t *buf;
307 size_t buflen;
308 ssize_t len;
309
310 ms = sess->mech_sess;
311 assert(ms->mech_sess.qop != QOP_NONE);
312 if (ms->mech_sess.qop == QOP_NONE)
313 return -1;
314
315 len = saslc__buffer_fetch(ms->enc_ctx, in, inlen, &buf, &buflen);
316 if (len == -1)
317 return -1;
318
319 if (buflen == 0) {
320 *out = NULL;
321 *outlen = 0;
322 return len;
323 }
324
325 input.value = buf;
326 input.length = buflen;
327 output.value = NULL;
328 output.length = 0;
329
330 maj_s = gss_wrap(&min_s, ms->gss_ctx, ms->mech_sess.qop == QOP_CONF,
331 GSS_C_QOP_DEFAULT, &input, NULL, &output);
332
333 if (GSS_ERROR(maj_s)) {
334 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
335 return -1;
336 }
337 if (prep_packet(sess, &output, out, outlen) == -1)
338 return -1;
339
340 return len;
341 }
342
343 /**
344 * @brief decodes one block of data using the negotiated security layer.
345 * @param sess sasl session
346 * @param in input data
347 * @param inlen input data length
348 * @param out place to store output data
349 * @param outlen output data length
350 * @return number of bytes consumed, zero if more needed, or -1 on failure.
351 */
352 static ssize_t
saslc__mech_gssapi_decode(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)353 saslc__mech_gssapi_decode(saslc_sess_t *sess, const void *in, size_t inlen,
354 void **out, size_t *outlen)
355 {
356 saslc__mech_gssapi_sess_t *ms;
357 gss_buffer_desc input, output;
358 OM_uint32 min_s, maj_s;
359 uint8_t *buf;
360 size_t buflen;
361 ssize_t len;
362
363 ms = sess->mech_sess;
364 assert(ms->mech_sess.qop != QOP_NONE);
365 if (ms->mech_sess.qop == QOP_NONE)
366 return -1;
367
368 len = saslc__buffer32_fetch(ms->dec_ctx, in, inlen, &buf, &buflen);
369 if (len == -1)
370 return -1;
371
372 if (buflen == 0) {
373 *out = NULL;
374 *outlen = 0;
375 return len;
376 }
377
378 /* buf -> szbuf (4 bytes) followed by the payload buffer */
379 input.value = buf + 4;
380 input.length = buflen - 4;
381 output.value = NULL;
382 output.length = 0;
383
384 maj_s = gss_unwrap(&min_s, ms->gss_ctx, &input, &output, NULL, NULL);
385
386 if (GSS_ERROR(maj_s)) {
387 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
388 return -1;
389 }
390
391 if (prep_output(sess, &output, out, outlen) == -1)
392 return -1;
393
394 return len;
395 }
396
397 /**
398 * @brief get service name from properties
399 * ("<servicename>@<hostname>") and store it in service token.
400 * @param sess the session context
401 * @param service the gs_name_t token to return service name in
402 * @return 0 on success, -1 on error
403 */
404 static int
get_service(saslc_sess_t * sess,gss_name_t * service)405 get_service(saslc_sess_t *sess, gss_name_t *service)
406 {
407 gss_buffer_desc bufdesc;
408 const char *hostname, *servicename;
409 char *buf;
410 int buflen;
411 OM_uint32 min_s, maj_s;
412
413 hostname = saslc_sess_getprop(sess, SASLC_GSSAPI_HOSTNAME);
414 if (hostname == NULL) {
415 saslc__error_set(ERR(sess), ERROR_MECH,
416 "hostname is required for an authentication");
417 return -1;
418 }
419 servicename = saslc_sess_getprop(sess, SASLC_GSSAPI_SERVICE);
420 if (servicename == NULL) {
421 saslc__error_set(ERR(sess), ERROR_MECH,
422 "service is required for an authentication");
423 return -1;
424 }
425 buflen = asprintf(&buf, "%s@%s", servicename, hostname);
426 if (buflen == -1) {
427 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
428 return -1;
429 }
430 bufdesc.value = buf;
431 bufdesc.length = buflen + 1;
432
433 saslc__msg_dbg("%s: buf='%s'", __func__, buf);
434
435 maj_s = gss_import_name(&min_s, &bufdesc, GSS_C_NT_HOSTBASED_SERVICE,
436 service);
437 free(buf);
438 if (GSS_ERROR(maj_s)) {
439 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
440 return -1;
441 }
442 return 0;
443 }
444
445 /**
446 * @brief gss_init_sec_context() wrapper
447 * @param sess session context
448 * @param inbuf input token
449 * @param outbuf output token
450 * @return 0 if GSS_S_COMPLETE, 1 if GSS_S_CONTINUE_NEEDED, -1 on failure
451 */
452 static int
init_sec_context(saslc_sess_t * sess,gss_buffer_t inbuf,gss_buffer_t outbuf)453 init_sec_context(saslc_sess_t *sess, gss_buffer_t inbuf, gss_buffer_t outbuf)
454 {
455 saslc__mech_gssapi_sess_t *ms;
456 OM_uint32 min_s, maj_s;
457
458 ms = sess->mech_sess;
459
460 outbuf->length = 0;
461 outbuf->value = NULL;
462 maj_s = gss_init_sec_context(
463 &min_s, /* minor status */
464 GSS_C_NO_CREDENTIAL, /* use current login context credential */
465 &ms->gss_ctx, /* initially GSS_C_NO_CONTEXT */
466 ms->server_name, /* server@hostname */
467 GSS_C_NO_OID, /* use default mechanism */
468 #if 1
469 GSS_C_REPLAY_FLAG | /* message replay detection */
470 GSS_C_INTEG_FLAG | /* request integrity */
471 GSS_C_CONF_FLAG | /* request confirmation */
472 #endif
473 GSS_C_MUTUAL_FLAG | /* mutual authentication */
474 GSS_C_SEQUENCE_FLAG, /* message sequence checking */
475 0, /* default lifetime (2 hrs) */
476 GSS_C_NO_CHANNEL_BINDINGS,
477 inbuf, /* input token */
478 /* output parameters follow */
479 NULL, /* mechanism type for context */
480 outbuf, /* output token */
481 NULL, /* services available for context */
482 NULL); /* lifetime of context */
483
484 switch (maj_s) {
485 case GSS_S_COMPLETE:
486 return 0;
487 case GSS_S_CONTINUE_NEEDED:
488 return 1;
489 default:
490 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
491 return -1;
492 }
493 }
494
495 /**
496 * @brief unwrap the authentication token received from the server.
497 * This contains the qop_mask and maxbuf values which are updated in
498 * saslc__mech_gssapi_sess_t.
499 * @param sess the session context
500 * @param inbuf the received authentication token.
501 * @return 0 on success, -1 on error.
502 */
503 static int
unwrap_input_token(saslc_sess_t * sess,gss_buffer_t inbuf)504 unwrap_input_token(saslc_sess_t *sess, gss_buffer_t inbuf)
505 {
506 saslc__mech_gssapi_sess_t *ms;
507 OM_uint32 min_s, maj_s;
508 gss_buffer_t outbuf;
509 gss_buffer_desc outdesc;
510 unsigned char *p;
511
512 /********************************************************************/
513 /* [RFC 2222 section 7.2.1] */
514 /* The client passes this token to GSS_Unwrap and interprets */
515 /* the first octet of resulting cleartext as a bit-mask specifying */
516 /* the security layers supported by the server and the second */
517 /* through fourth octets as the maximum size output_message to send */
518 /* to the server. */
519 /********************************************************************/
520
521 ms = sess->mech_sess;
522
523 outbuf = &outdesc;
524 maj_s = gss_unwrap(&min_s, ms->gss_ctx, inbuf, outbuf, NULL, NULL);
525
526 if (GSS_ERROR(maj_s)) {
527 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
528 return -1;
529 }
530 if (outbuf->length != 4) {
531 saslc__error_set(ERR(sess), ERROR_MECH,
532 "invalid unwrap length");
533 return -1;
534 }
535 p = outbuf->value;
536 ms->qop_mask = p[0];
537 ms->omaxbuf = (be32dec(p) & 0xffffff);
538
539 saslc__msg_dbg("%s: qop_mask=0x%02x omaxbuf=%d",
540 __func__, ms->qop_mask, ms->omaxbuf);
541
542 if (ms->qop_mask == QOP_NONE && ms->omaxbuf != 0) {
543 saslc__error_set(ERR(sess), ERROR_MECH,
544 "server has no security layer support, but maxbuf != 0");
545 return -1;
546 }
547 maj_s = gss_release_buffer(&min_s, outbuf);
548 if (GSS_ERROR(maj_s)) {
549 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
550 return -1;
551 }
552 return 0;
553 }
554
555 /**
556 * @brief construct and wrap up an authentication token and put it in
557 * outbuf. The outbuf token data is structured as follows:
558 * struct {
559 * uint8_t qop; // qop to use
560 * uint8_t maxbuf[3] // maxbuf for client (network byte order)
561 * uint8_t authcid[] // variable length authentication id (username)
562 * } __packed;
563 * @param sess the session
564 * @param outbuf the gss_buffer_t token to return to server.
565 * @return 0 on success, -1 on error.
566 */
567 static int
wrap_output_token(saslc_sess_t * sess,gss_buffer_t outbuf)568 wrap_output_token(saslc_sess_t *sess, gss_buffer_t outbuf)
569 {
570 saslc__mech_gssapi_sess_t *ms;
571 gss_buffer_desc indesc;
572 char *input_value;
573 int len;
574 const char *authcid;
575 OM_uint32 min_s, maj_s;
576 unsigned char *p;
577
578 /********************************************************************/
579 /* [RFC 2222 section 7.2.1] */
580 /* The client then constructs data, with the first octet containing */
581 /* the bit-mask specifying the selected security layer, the second */
582 /* through fourth octets containing in network byte order the */
583 /* maximum size output_message the client is able to receive, and */
584 /* the remaining octets containing the authorization identity. The */
585 /* authorization identity is optional in mechanisms where it is */
586 /* encoded in the exchange such as GSSAPI. The client passes the */
587 /* data to GSS_Wrap with conf_flag set to FALSE, and responds with */
588 /* the generated output_message. The client can then consider the */
589 /* server authenticated. */
590 /********************************************************************/
591
592 ms = sess->mech_sess;
593
594 authcid = saslc_sess_getprop(sess, SASLC_GSSAPI_AUTHCID);
595
596 len = asprintf(&input_value, "qmax%s", authcid ? authcid : "");
597 if (len == -1) {
598 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
599 return -1;
600 }
601 be32enc(input_value, ms->imaxbuf);
602 input_value[0] = saslc__mech_qop_flag(ms->mech_sess.qop);
603
604 indesc.value = input_value;
605 indesc.length = len; /* XXX: don't count the '\0' */
606
607 p = (unsigned char *)input_value;
608 saslc__msg_dbg("%s: input_value='%02x %02x %02x %02x %s",
609 __func__, p[0], p[1], p[2], p[3], input_value + 4);
610
611 maj_s = gss_wrap(&min_s, ms->gss_ctx, 0 /* FALSE - RFC2222 */,
612 GSS_C_QOP_DEFAULT, &indesc, NULL, outbuf);
613
614 free(input_value);
615
616 if (GSS_ERROR(maj_s)) {
617 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
618 return -1;
619 }
620 return 0;
621 }
622
623 /************************************************************************
624 * XXX: Share this with mech_digestmd5.c? They are almost identical.
625 */
626 /**
627 * @brief choose the best qop based on what was provided by the
628 * challenge and a possible user mask.
629 * @param sess the session context
630 * @param qop_flags the qop flags parsed from the challenge string
631 * @return the selected saslc__mech_sess_qop_t or -1 if no match
632 */
633 static int
choose_qop(saslc_sess_t * sess,uint32_t qop_flags)634 choose_qop(saslc_sess_t *sess, uint32_t qop_flags)
635 {
636 list_t *list;
637 const char *user_qop;
638
639 qop_flags &= DEFAULT_QOP_MASK;
640 user_qop = saslc_sess_getprop(sess, SASLC_GSSAPI_QOPMASK);
641 if (user_qop != NULL) {
642 if (saslc__list_parse(&list, user_qop) == -1) {
643 saslc__error_set_errno(ERR(sess), ERROR_NOMEM);
644 return -1;
645 }
646 qop_flags &= saslc__mech_qop_list_flags(list);
647 saslc__list_free(list);
648 }
649
650 /*
651 * Select the most secure supported qop.
652 */
653 if ((qop_flags & F_QOP_CONF) != 0)
654 return QOP_CONF;
655 if ((qop_flags & F_QOP_INT) != 0)
656 return QOP_INT;
657 if ((qop_flags & F_QOP_NONE) != 0)
658 return QOP_NONE;
659
660 saslc__error_set(ERR(sess), ERROR_MECH,
661 "cannot choose an acceptable qop");
662 return -1;
663 }
664 /************************************************************************/
665
666 /**
667 * @brief compute the maximum buffer length we can use and not
668 * overflow the servers maxbuf.
669 * @param sess the session context
670 * @param maxbuf the server's maxbuf value
671 */
672 static int
wrap_size_limit(saslc_sess_t * sess,OM_uint32 maxbuf)673 wrap_size_limit(saslc_sess_t *sess, OM_uint32 maxbuf)
674 {
675 saslc__mech_gssapi_sess_t *ms;
676 OM_uint32 min_s, maj_s;
677 OM_uint32 max_input;
678
679 ms = sess->mech_sess;
680
681 maj_s = gss_wrap_size_limit(&min_s, ms->gss_ctx, 1, GSS_C_QOP_DEFAULT,
682 maxbuf, &max_input);
683
684 if (GSS_ERROR(maj_s)) {
685 saslc__mech_gssapi_set_err(sess, ERROR_MECH, maj_s, min_s);
686 return -1;
687 }
688
689 /* XXX: from cyrus-sasl: gssapi.c */
690 if (max_input > maxbuf) {
691 /* Heimdal appears to get this wrong */
692 maxbuf -= (max_input - maxbuf);
693 } else {
694 /* This code is actually correct */
695 maxbuf = max_input;
696 }
697 return maxbuf;
698 }
699
700 /**
701 * @brief set our imaxbuf (from omaxbuf or from properties) and
702 * then reset omaxbuf in saslc__mech_gssapi_sess_t.
703 * @param sess the session context
704 * @return 0 on success, -1 on error
705 *
706 * Note: on entry the omaxbuf is the server's maxbuf size. On exit
707 * the omaxbuf is the maximum buffer we can fill that will not
708 * overflow the servers maxbuf after it is encoded. This value is
709 * given by wrap_size_limit().
710 */
711 static int
set_maxbufs(saslc_sess_t * sess)712 set_maxbufs(saslc_sess_t *sess)
713 {
714 saslc__mech_gssapi_sess_t *ms;
715 const char *p;
716 char *q;
717 unsigned long val;
718 int rv;
719
720 ms = sess->mech_sess;
721
722 /* by default, we use the same input maxbuf as the server. */
723 ms->imaxbuf = ms->omaxbuf;
724 p = saslc_sess_getprop(sess, SASLC_PROP_MAXBUF);
725 if (p != NULL) {
726 val = strtol(p, &q, 0);
727 if (p[0] == '\0' || *q != '\0') {
728
729 return MECH_ERROR;
730 }
731 if (errno == ERANGE && val == ULONG_MAX) {
732
733 return MECH_ERROR;
734 }
735 if (val > 0xffffff)
736 val = 0xffffff;
737 ms->imaxbuf = (uint32_t)val;
738 }
739 rv = wrap_size_limit(sess, ms->omaxbuf);
740 if (rv == -1)
741 return MECH_ERROR;
742 ms->omaxbuf = rv; /* maxbuf size for unencoded output data */
743
744 return 0;
745 }
746
747 /**
748 * @brief do one step of the sasl authentication
749 * @param sess sasl session
750 * @param in input data
751 * @param inlen input data length
752 * @param out place to store output data
753 * @param outlen output data length
754 * @return MECH_OK on success, MECH_STEP if more steps are needed,
755 * MECH_ERROR on failure
756 */
757 static int
saslc__mech_gssapi_cont(saslc_sess_t * sess,const void * in,size_t inlen,void ** out,size_t * outlen)758 saslc__mech_gssapi_cont(saslc_sess_t *sess, const void *in, size_t inlen,
759 void **out, size_t *outlen)
760 {
761 saslc__mech_gssapi_sess_t *ms;
762 gss_buffer_desc input, output;
763 int rv;
764
765 /**************************************************************************/
766 /* [RFC 2222 section 7.2.1] */
767 /* The client calls GSS_Init_sec_context, passing in 0 for */
768 /* input_context_handle (initially) and a targ_name equal to output_name */
769 /* from GSS_Import_Name called with input_name_type of */
770 /* GSS_C_NT_HOSTBASED_SERVICE and input_name_string of */
771 /* "service@hostname" where "service" is the service name specified in */
772 /* the protocol's profile, and "hostname" is the fully qualified host */
773 /* name of the server. The client then responds with the resulting */
774 /* output_token. If GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED, */
775 /* then the client should expect the server to issue a token in a */
776 /* subsequent challenge. The client must pass the token to another call */
777 /* to GSS_Init_sec_context, repeating the actions in this paragraph. */
778 /* */
779 /* When GSS_Init_sec_context returns GSS_S_COMPLETE, the client takes */
780 /* the following actions: If the last call to GSS_Init_sec_context */
781 /* returned an output_token, then the client responds with the */
782 /* output_token, otherwise the client responds with no data. The client */
783 /* should then expect the server to issue a token in a subsequent */
784 /* challenge. The client passes this token to GSS_Unwrap and interprets */
785 /* the first octet of resulting cleartext as a bit-mask specifying the */
786 /* security layers supported by the server and the second through fourth */
787 /* octets as the maximum size output_message to send to the server. The */
788 /* client then constructs data, with the first octet containing the */
789 /* bit-mask specifying the selected security layer, the second through */
790 /* fourth octets containing in network byte order the maximum size */
791 /* output_message the client is able to receive, and the remaining */
792 /* octets containing the authorization identity. The client passes the */
793 /* data to GSS_Wrap with conf_flag set to FALSE, and responds with the */
794 /* generated output_message. The client can then consider the server */
795 /* authenticated. */
796 /**************************************************************************/
797
798 ms = sess->mech_sess;
799
800 switch(ms->status) {
801 case GSSAPI_AUTH_FIRST:
802 saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_FIRST");
803
804 if (get_service(sess, &ms->server_name) == -1)
805 return MECH_ERROR;
806
807 rv = init_sec_context(sess, GSS_C_NO_BUFFER, &output);
808 if (rv == -1)
809 return MECH_ERROR;
810
811 if (prep_output(sess, &output, out, outlen) == -1)
812 return MECH_ERROR;
813
814 ms->status = rv == 0 ? GSSAPI_AUTH_LAST : GSSAPI_AUTH_NEXT;
815 return MECH_STEP;
816
817 case GSSAPI_AUTH_NEXT:
818 saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_NEXT");
819
820 input.value = __UNCONST(in);
821 input.length = inlen;
822 if ((rv = init_sec_context(sess, &input, &output)) == -1)
823 return MECH_ERROR;
824
825 if (prep_output(sess, &output, out, outlen) == -1)
826 return MECH_ERROR;
827
828 if (rv == 0)
829 ms->status = GSSAPI_AUTH_LAST;
830 return MECH_STEP;
831
832 case GSSAPI_AUTH_LAST:
833 saslc__msg_dbg("%s: status: %s", __func__, "GSSAPI_AUTH_LAST");
834
835 input.value = __UNCONST(in);
836 input.length = inlen;
837 if (unwrap_input_token(sess, &input) == -1)
838 return MECH_ERROR;
839
840 if ((rv = choose_qop(sess, ms->qop_mask)) == -1)
841 return MECH_ERROR;
842
843 ms->mech_sess.qop = rv;
844
845 if (ms->mech_sess.qop != QOP_NONE) {
846 if (ms->mech_sess.qop == QOP_CONF) {
847 /*
848 * XXX: where do we negotiate the cipher,
849 * or do we?
850 */
851 }
852 if (set_maxbufs(sess) == -1)
853 return MECH_ERROR;
854 ms->dec_ctx = saslc__buffer32_create(sess, ms->imaxbuf);
855 ms->enc_ctx = saslc__buffer_create(sess, ms->omaxbuf);
856 }
857 if (wrap_output_token(sess, &output) == -1)
858 return MECH_ERROR;
859
860 if (prep_output(sess, &output, out, outlen) == -1)
861 return MECH_ERROR;
862
863 ms->status = GSSAPI_AUTH_DONE;
864 return MECH_OK;
865
866 case GSSAPI_AUTH_DONE:
867 assert(/*CONSTCOND*/0); /* XXX: impossible */
868 saslc__error_set(ERR(sess), ERROR_MECH,
869 "already authenticated");
870 return MECH_ERROR;
871
872 #if 0 /* no default so the compiler can tell us if we miss an enum */
873 default:
874 assert(/*CONSTCOND*/0); /* impossible */
875 /*NOTREACHED*/
876 #endif
877 }
878 /*LINTED*/
879 assert(/*CONSTCOND*/0); /* XXX: impossible */
880 return MECH_ERROR;
881 }
882
883 /* mechanism definition */
884 const saslc__mech_t saslc__mech_gssapi = {
885 .name = "GSSAPI",
886 .flags = FLAG_NONE,
887 .create = saslc__mech_gssapi_create,
888 .cont = saslc__mech_gssapi_cont,
889 .encode = saslc__mech_gssapi_encode,
890 .decode = saslc__mech_gssapi_decode,
891 .destroy = saslc__mech_gssapi_destroy
892 };
893