1 /*
2 * Copyright (c) 2010, JANET(UK)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of JANET(UK) nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32 /*
33 * Copyright (c) 1998-2016 Carnegie Mellon University.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 *
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in
45 * the documentation and/or other materials provided with the
46 * distribution.
47 *
48 * 3. The name "Carnegie Mellon University" must not be used to
49 * endorse or promote products derived from this software without
50 * prior written permission. For permission or any other legal
51 * details, please contact
52 * Carnegie Mellon University
53 * Center for Technology Transfer and Enterprise Creation
54 * 4615 Forbes Avenue
55 * Suite 302
56 * Pittsburgh, PA 15213
57 * (412) 268-7393, fax: (412) 268-7395
58 * innovation@andrew.cmu.edu
59 *
60 * 4. Redistributions of any form whatsoever must retain the following
61 * acknowledgment:
62 * "This product includes software developed by Computing Services
63 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
64 *
65 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
66 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
67 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
68 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
69 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
70 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
71 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
72 */
73
74 #include <config.h>
75 #include <gssapi/gssapi.h>
76
77 #ifndef KRB5_HEIMDAL
78 #ifdef HAVE_GSSAPI_GSSAPI_EXT_H
79 #include <gssapi/gssapi_ext.h>
80 #endif
81 #endif
82
83 #include <fcntl.h>
84 #include <stdio.h>
85 #include <sasl.h>
86 #include <saslutil.h>
87 #include <saslplug.h>
88
89 #include "plugin_common.h"
90
91 #ifdef HAVE_UNISTD_H
92 #include <unistd.h>
93 #endif
94
95 #include <errno.h>
96 #include <assert.h>
97 #include "gs2_token.h"
98
99 #define GS2_CB_FLAG_MASK 0x0F
100 #define GS2_CB_FLAG_N 0x00
101 #define GS2_CB_FLAG_P 0x01
102 #define GS2_CB_FLAG_Y 0x02
103 #define GS2_NONSTD_FLAG 0x10
104
105 typedef struct context {
106 gss_ctx_id_t gss_ctx;
107 gss_name_t client_name;
108 gss_name_t server_name;
109 gss_cred_id_t server_creds;
110 gss_cred_id_t client_creds;
111 char *out_buf;
112 unsigned out_buf_len;
113 const sasl_utils_t *utils;
114 char *authid;
115 char *authzid;
116 union {
117 sasl_client_plug_t *client;
118 sasl_server_plug_t *server;
119 } plug;
120 gss_OID mechanism;
121 int gs2_flags;
122 char *cbindingname;
123 struct gss_channel_bindings_struct gss_cbindings;
124 sasl_secret_t *password;
125 unsigned int free_password;
126 OM_uint32 lifetime;
127 } context_t;
128
129 static gss_OID_set gs2_mechs = GSS_C_NO_OID_SET;
130
131 static int gs2_get_init_creds(context_t *context,
132 sasl_client_params_t *params,
133 sasl_interact_t **prompt_need,
134 sasl_out_params_t *oparams);
135
136 static int gs2_verify_initial_message(context_t *text,
137 sasl_server_params_t *sparams,
138 const char *in,
139 unsigned inlen,
140 gss_buffer_t token);
141
142 static int gs2_make_header(context_t *text,
143 sasl_client_params_t *cparams,
144 const char *authzid,
145 char **out,
146 unsigned *outlen);
147
148 static int gs2_make_message(context_t *text,
149 sasl_client_params_t *cparams,
150 int initialContextToken,
151 gss_buffer_t token,
152 char **out,
153 unsigned *outlen);
154
155 static int gs2_get_mech_attrs(const sasl_utils_t *utils,
156 const gss_OID mech,
157 unsigned int *security_flags,
158 unsigned int *features,
159 const unsigned long **prompts);
160
161 static int gs2_indicate_mechs(const sasl_utils_t *utils);
162
163 static int gs2_map_sasl_name(const sasl_utils_t *utils,
164 const char *mech,
165 gss_OID *oid);
166
167 static int gs2_duplicate_buffer(const sasl_utils_t *utils,
168 const gss_buffer_t src,
169 gss_buffer_t dst);
170
171 static int gs2_unescape_authzid(const sasl_utils_t *utils,
172 char **in,
173 unsigned *inlen,
174 char **authzid);
175
176 static int gs2_escape_authzid(const sasl_utils_t *utils,
177 const char *in,
178 unsigned inlen,
179 char **authzid);
180
181 /* sasl_gs_log: only logs status string returned from gss_display_status() */
182 #define sasl_gs2_log(x,y,z) sasl_gs2_seterror_(x,y,z,1)
183 #define sasl_gs2_seterror(x,y,z) sasl_gs2_seterror_(x,y,z,0)
184
185 static int
186 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
187 int logonly);
188
189 static context_t *
sasl_gs2_new_context(const sasl_utils_t * utils)190 sasl_gs2_new_context(const sasl_utils_t *utils)
191 {
192 context_t *ret;
193
194 ret = utils->malloc(sizeof(context_t));
195 if (ret == NULL)
196 return NULL;
197
198 memset(ret, 0, sizeof(context_t));
199 ret->utils = utils;
200
201 return ret;
202 }
203
204 static int
sasl_gs2_free_context_contents(context_t * text)205 sasl_gs2_free_context_contents(context_t *text)
206 {
207 OM_uint32 min_stat;
208
209 if (text == NULL)
210 return SASL_OK;
211
212 if (text->gss_ctx != GSS_C_NO_CONTEXT) {
213 gss_delete_sec_context(&min_stat,&text->gss_ctx,
214 GSS_C_NO_BUFFER);
215 text->gss_ctx = GSS_C_NO_CONTEXT;
216 }
217
218 if (text->client_name != GSS_C_NO_NAME) {
219 gss_release_name(&min_stat,&text->client_name);
220 text->client_name = GSS_C_NO_NAME;
221 }
222
223 if (text->server_name != GSS_C_NO_NAME) {
224 gss_release_name(&min_stat,&text->server_name);
225 text->server_name = GSS_C_NO_NAME;
226 }
227
228 if (text->server_creds != GSS_C_NO_CREDENTIAL) {
229 gss_release_cred(&min_stat, &text->server_creds);
230 text->server_creds = GSS_C_NO_CREDENTIAL;
231 }
232
233 if (text->client_creds != GSS_C_NO_CREDENTIAL) {
234 gss_release_cred(&min_stat, &text->client_creds);
235 text->client_creds = GSS_C_NO_CREDENTIAL;
236 }
237
238 if (text->authid != NULL) {
239 text->utils->free(text->authid);
240 text->authid = NULL;
241 }
242
243 if (text->authzid != NULL) {
244 text->utils->free(text->authzid);
245 text->authzid = NULL;
246 }
247
248 gss_release_buffer(&min_stat, &text->gss_cbindings.application_data);
249
250 if (text->out_buf != NULL) {
251 text->utils->free(text->out_buf);
252 text->out_buf = NULL;
253 }
254
255 text->out_buf_len = 0;
256
257 if (text->cbindingname != NULL) {
258 text->utils->free(text->cbindingname);
259 text->cbindingname = NULL;
260 }
261
262 if (text->free_password)
263 _plug_free_secret(text->utils, &text->password);
264
265 memset(text, 0, sizeof(*text));
266
267 return SASL_OK;
268 }
269
270 static void
gs2_common_mech_dispose(void * conn_context,const sasl_utils_t * utils)271 gs2_common_mech_dispose(void *conn_context, const sasl_utils_t *utils)
272 {
273 sasl_gs2_free_context_contents((context_t *)(conn_context));
274 utils->free(conn_context);
275 }
276
277 static void
gs2_common_mech_free(void * global_context,const sasl_utils_t * utils)278 gs2_common_mech_free(void *global_context __attribute__((unused)),
279 const sasl_utils_t *utils __attribute__((unused)))
280 {
281 OM_uint32 minor;
282
283 if (gs2_mechs != GSS_C_NO_OID_SET) {
284 gss_release_oid_set(&minor, &gs2_mechs);
285 gs2_mechs = GSS_C_NO_OID_SET;
286 }
287 }
288
289 /***************************** Server Section *****************************/
290
291 static int
gs2_server_mech_new(void * glob_context,sasl_server_params_t * params,const char * challenge,unsigned challen,void ** conn_context)292 gs2_server_mech_new(void *glob_context,
293 sasl_server_params_t *params,
294 const char *challenge __attribute__((unused)),
295 unsigned challen __attribute__((unused)),
296 void **conn_context)
297 {
298 context_t *text;
299 int ret;
300
301 text = sasl_gs2_new_context(params->utils);
302 if (text == NULL) {
303 MEMERROR(params->utils);
304 return SASL_NOMEM;
305 }
306
307 text->gss_ctx = GSS_C_NO_CONTEXT;
308 text->client_name = GSS_C_NO_NAME;
309 text->server_name = GSS_C_NO_NAME;
310 text->server_creds = GSS_C_NO_CREDENTIAL;
311 text->client_creds = GSS_C_NO_CREDENTIAL;
312 text->plug.server = glob_context;
313
314 ret = gs2_map_sasl_name(params->utils, text->plug.server->mech_name,
315 &text->mechanism);
316 if (ret != SASL_OK) {
317 gs2_common_mech_dispose(text, params->utils);
318 return ret;
319 }
320
321 *conn_context = text;
322
323 return SASL_OK;
324 }
325
326 static int
gs2_server_mech_step(void * conn_context,sasl_server_params_t * params,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)327 gs2_server_mech_step(void *conn_context,
328 sasl_server_params_t *params,
329 const char *clientin,
330 unsigned clientinlen,
331 const char **serverout,
332 unsigned *serveroutlen,
333 sasl_out_params_t *oparams)
334 {
335 context_t *text = (context_t *)conn_context;
336 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
337 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
338 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
339 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
340 gss_buffer_desc short_name_buf = GSS_C_EMPTY_BUFFER;
341 gss_name_t without = GSS_C_NO_NAME;
342 gss_OID_set_desc mechs;
343 OM_uint32 out_flags = 0;
344 int ret = SASL_OK, equal = 0;
345 int initialContextToken = (text->gss_ctx == GSS_C_NO_CONTEXT);
346 char *p;
347
348 if (serverout == NULL) {
349 PARAMERROR(text->utils);
350 return SASL_BADPARAM;
351 }
352
353 *serverout = NULL;
354 *serveroutlen = 0;
355
356 if (initialContextToken) {
357 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
358 name_buf.value = params->utils->malloc(name_buf.length + 1);
359 if (name_buf.value == NULL) {
360 MEMERROR(text->utils);
361 ret = SASL_NOMEM;
362 goto cleanup;
363 }
364 snprintf(name_buf.value, name_buf.length + 1,
365 "%s@%s", params->service, params->serverFQDN);
366 maj_stat = gss_import_name(&min_stat,
367 &name_buf,
368 GSS_C_NT_HOSTBASED_SERVICE,
369 &text->server_name);
370 params->utils->free(name_buf.value);
371 name_buf.value = NULL;
372
373 if (GSS_ERROR(maj_stat))
374 goto cleanup;
375
376 assert(text->server_creds == GSS_C_NO_CREDENTIAL);
377
378 mechs.count = 1;
379 mechs.elements = (gss_OID)text->mechanism;
380
381 if (params->gss_creds == GSS_C_NO_CREDENTIAL) {
382 maj_stat = gss_acquire_cred(&min_stat,
383 text->server_name,
384 GSS_C_INDEFINITE,
385 &mechs,
386 GSS_C_ACCEPT,
387 &text->server_creds,
388 NULL,
389 &text->lifetime);
390 if (GSS_ERROR(maj_stat))
391 goto cleanup;
392 }
393
394 ret = gs2_verify_initial_message(text,
395 params,
396 clientin,
397 clientinlen,
398 &input_token);
399 if (ret != SASL_OK)
400 goto cleanup;
401 } else {
402 input_token.value = (void *)clientin;
403 input_token.length = clientinlen;
404 }
405
406 maj_stat = gss_accept_sec_context(&min_stat,
407 &text->gss_ctx,
408 (params->gss_creds != GSS_C_NO_CREDENTIAL)
409 ? (gss_cred_id_t)params->gss_creds
410 : text->server_creds,
411 &input_token,
412 &text->gss_cbindings,
413 &text->client_name,
414 NULL,
415 &output_token,
416 &out_flags,
417 &text->lifetime,
418 &text->client_creds);
419 if (GSS_ERROR(maj_stat)) {
420 sasl_gs2_log(text->utils, maj_stat, min_stat);
421 text->utils->seterror(text->utils->conn, SASL_NOLOG,
422 "GS2 Failure: gss_accept_sec_context");
423 ret = (maj_stat == GSS_S_BAD_BINDINGS) ? SASL_BADBINDING : SASL_BADAUTH;
424 goto cleanup;
425 }
426
427 *serveroutlen = output_token.length;
428 if (output_token.value != NULL) {
429 ret = _plug_buf_alloc(text->utils, &text->out_buf,
430 &text->out_buf_len, *serveroutlen);
431 if (ret != SASL_OK)
432 goto cleanup;
433 memcpy(text->out_buf, output_token.value, *serveroutlen);
434 *serverout = text->out_buf;
435 } else {
436 /* No output token, send an empty string */
437 *serverout = "";
438 serveroutlen = 0;
439 }
440
441 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
442 ret = SASL_CONTINUE;
443 goto cleanup;
444 }
445
446 assert(maj_stat == GSS_S_COMPLETE);
447
448 if ((out_flags & GSS_C_SEQUENCE_FLAG) == 0) {
449 ret = SASL_BADAUTH;
450 goto cleanup;
451 }
452
453 maj_stat = gss_display_name(&min_stat, text->client_name,
454 &name_buf, NULL);
455 if (GSS_ERROR(maj_stat))
456 goto cleanup;
457
458 ret = gs2_duplicate_buffer(params->utils, &name_buf, &short_name_buf);
459 if (ret != 0)
460 goto cleanup;
461
462 p = (char *)memchr(name_buf.value, '@', name_buf.length);
463 if (p != NULL) {
464 short_name_buf.length = (p - (char *)name_buf.value);
465
466 maj_stat = gss_import_name(&min_stat,
467 &short_name_buf,
468 GSS_C_NT_USER_NAME,
469 &without);
470 if (GSS_ERROR(maj_stat)) {
471 goto cleanup;
472 }
473
474 maj_stat = gss_compare_name(&min_stat, text->client_name,
475 without, &equal);
476 if (GSS_ERROR(maj_stat)) {
477 goto cleanup;
478 }
479
480 if (equal)
481 ((char *)short_name_buf.value)[short_name_buf.length] = '\0';
482 }
483
484 text->authid = (char *)short_name_buf.value;
485 short_name_buf.value = NULL;
486 short_name_buf.length = 0;
487
488 if (text->authzid != NULL) {
489 ret = params->canon_user(params->utils->conn,
490 text->authzid, 0,
491 SASL_CU_AUTHZID, oparams);
492 if (ret != SASL_OK) {
493 goto cleanup;
494 }
495 }
496
497 ret = params->canon_user(params->utils->conn,
498 text->authid, 0,
499 text->authzid == NULL
500 ? (SASL_CU_AUTHZID | SASL_CU_AUTHID)
501 : SASL_CU_AUTHID,
502 oparams);
503 if (ret != SASL_OK) {
504 goto cleanup;
505 }
506
507 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
508 case GS2_CB_FLAG_N:
509 oparams->cbindingdisp = SASL_CB_DISP_NONE;
510 break;
511 case GS2_CB_FLAG_P:
512 oparams->cbindingdisp = SASL_CB_DISP_USED;
513 oparams->cbindingname = text->cbindingname;
514 break;
515 case GS2_CB_FLAG_Y:
516 oparams->cbindingdisp = SASL_CB_DISP_WANT;
517 break;
518 }
519
520 if (text->client_creds != GSS_C_NO_CREDENTIAL)
521 oparams->client_creds = &text->client_creds;
522 else
523 oparams->client_creds = NULL;
524
525 oparams->gss_peer_name = text->client_name;
526 oparams->gss_local_name = text->server_name;
527 oparams->maxoutbuf = 0xFFFFFF;
528 oparams->encode = NULL;
529 oparams->decode = NULL;
530 oparams->mech_ssf = 0;
531 oparams->doneflag = 1;
532
533 ret = SASL_OK;
534
535 cleanup:
536 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
537 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
538 ret = SASL_FAIL;
539 }
540
541 if (initialContextToken) {
542 gss_release_buffer(&min_stat, &input_token);
543 }
544 gss_release_buffer(&min_stat, &name_buf);
545 gss_release_buffer(&min_stat, &short_name_buf);
546 gss_release_buffer(&min_stat, &output_token);
547 gss_release_name(&min_stat, &without);
548
549 if (ret < SASL_OK) {
550 sasl_gs2_free_context_contents(text);
551 }
552
553 return ret;
554 }
555
556 static int
gs2_common_plug_init(const sasl_utils_t * utils,size_t plugsize,int (* plug_alloc)(const sasl_utils_t *,void *,const gss_buffer_t,const gss_OID),void ** pluglist,int * plugcount)557 gs2_common_plug_init(const sasl_utils_t *utils,
558 size_t plugsize,
559 int (*plug_alloc)(const sasl_utils_t *,
560 void *,
561 const gss_buffer_t,
562 const gss_OID),
563 void **pluglist,
564 int *plugcount)
565 {
566 OM_uint32 major, minor;
567 size_t i, count = 0;
568 void *plugs = NULL;
569
570 *pluglist = NULL;
571 *plugcount = 0;
572
573 if (gs2_indicate_mechs(utils) != SASL_OK) {
574 return SASL_NOMECH;
575 }
576
577 plugs = utils->malloc(gs2_mechs->count * plugsize);
578 if (plugs == NULL) {
579 MEMERROR(utils);
580 return SASL_NOMEM;
581 }
582 memset(plugs, 0, gs2_mechs->count * plugsize);
583
584 for (i = 0; i < gs2_mechs->count; i++) {
585 gss_buffer_desc sasl_mech_name = GSS_C_EMPTY_BUFFER;
586
587 major = gss_inquire_saslname_for_mech(&minor,
588 &gs2_mechs->elements[i],
589 &sasl_mech_name,
590 GSS_C_NO_BUFFER,
591 GSS_C_NO_BUFFER);
592 if (GSS_ERROR(major))
593 continue;
594
595 #define PLUG_AT(index) (void *)((unsigned char *)plugs + (count * plugsize))
596
597 if (plug_alloc(utils, PLUG_AT(count), &sasl_mech_name,
598 &gs2_mechs->elements[i]) == SASL_OK)
599 count++;
600
601 gss_release_buffer(&minor, &sasl_mech_name);
602 }
603
604 if (count == 0) {
605 utils->free(plugs);
606 return SASL_NOMECH;
607 }
608
609 *pluglist = plugs;
610 *plugcount = count;
611
612 return SASL_OK;
613 }
614
615 static int
gs2_server_plug_alloc(const sasl_utils_t * utils,void * plug,gss_buffer_t sasl_name,gss_OID mech)616 gs2_server_plug_alloc(const sasl_utils_t *utils,
617 void *plug,
618 gss_buffer_t sasl_name,
619 gss_OID mech)
620 {
621 int ret;
622 sasl_server_plug_t *splug = (sasl_server_plug_t *)plug;
623 gss_buffer_desc buf;
624
625 memset(splug, 0, sizeof(*splug));
626
627 ret = gs2_get_mech_attrs(utils, mech,
628 &splug->security_flags,
629 &splug->features,
630 NULL);
631 if (ret != SASL_OK)
632 return ret;
633
634 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
635 if (ret != SASL_OK)
636 return ret;
637
638 splug->mech_name = (char *)buf.value;
639 splug->glob_context = plug;
640 splug->mech_new = gs2_server_mech_new;
641 splug->mech_step = gs2_server_mech_step;
642 splug->mech_dispose = gs2_common_mech_dispose;
643 splug->mech_free = gs2_common_mech_free;
644
645 return SASL_OK;
646 }
647
648 static sasl_server_plug_t *gs2_server_plugins;
649 static int gs2_server_plugcount;
650
651 int
gs2_server_plug_init(const sasl_utils_t * utils,int maxversion,int * outversion,sasl_server_plug_t ** pluglist,int * plugcount)652 gs2_server_plug_init(const sasl_utils_t *utils,
653 int maxversion,
654 int *outversion,
655 sasl_server_plug_t **pluglist,
656 int *plugcount)
657 {
658 int ret;
659
660 *pluglist = NULL;
661 *plugcount = 0;
662
663 if (maxversion < SASL_SERVER_PLUG_VERSION)
664 return SASL_BADVERS;
665
666 *outversion = SASL_SERVER_PLUG_VERSION;
667
668 if (gs2_server_plugins == NULL) {
669 ret = gs2_common_plug_init(utils,
670 sizeof(sasl_server_plug_t),
671 gs2_server_plug_alloc,
672 (void **)&gs2_server_plugins,
673 &gs2_server_plugcount);
674 if (ret != SASL_OK)
675 return ret;
676 }
677
678 *pluglist = gs2_server_plugins;
679 *plugcount = gs2_server_plugcount;
680
681 return SASL_OK;
682 }
683
684 /***************************** Client Section *****************************/
685
gs2_client_mech_step(void * conn_context,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)686 static int gs2_client_mech_step(void *conn_context,
687 sasl_client_params_t *params,
688 const char *serverin,
689 unsigned serverinlen,
690 sasl_interact_t **prompt_need,
691 const char **clientout,
692 unsigned *clientoutlen,
693 sasl_out_params_t *oparams)
694 {
695 context_t *text = (context_t *)conn_context;
696 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
697 gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
698 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
699 OM_uint32 maj_stat = GSS_S_FAILURE, min_stat = 0;
700 OM_uint32 req_flags, ret_flags;
701 int ret = SASL_FAIL;
702 int initialContextToken;
703
704 *clientout = NULL;
705 *clientoutlen = 0;
706
707 if (text->gss_ctx == GSS_C_NO_CONTEXT) {
708 ret = gs2_get_init_creds(text, params, prompt_need, oparams);
709 if (ret != SASL_OK) {
710 goto cleanup;
711 }
712
713 initialContextToken = 1;
714 } else {
715 initialContextToken = 0;
716 }
717
718 if (text->server_name == GSS_C_NO_NAME) { /* only once */
719 if (params->serverFQDN == NULL ||
720 strlen(params->serverFQDN) == 0) {
721 SETERROR(text->utils, "GS2 Failure: no serverFQDN");
722 ret = SASL_FAIL;
723 goto cleanup;
724 }
725
726 name_buf.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
727 name_buf.value = params->utils->malloc(name_buf.length + 1);
728 if (name_buf.value == NULL) {
729 ret = SASL_NOMEM;
730 goto cleanup;
731 }
732 snprintf(name_buf.value, name_buf.length + 1,
733 "%s@%s", params->service, params->serverFQDN);
734
735 maj_stat = gss_import_name(&min_stat,
736 &name_buf,
737 GSS_C_NT_HOSTBASED_SERVICE,
738 &text->server_name);
739 params->utils->free(name_buf.value);
740 name_buf.value = NULL;
741
742 if (GSS_ERROR(maj_stat)) {
743 ret = SASL_OK;
744 goto cleanup;
745 }
746 }
747
748 /* From GSSAPI plugin: apparently this is for some IMAP bug workaround */
749 if (serverinlen == 0 && text->gss_ctx != GSS_C_NO_CONTEXT) {
750 gss_delete_sec_context(&min_stat, &text->gss_ctx, GSS_C_NO_BUFFER);
751 text->gss_ctx = GSS_C_NO_CONTEXT;
752 }
753
754 input_token.value = (void *)serverin;
755 input_token.length = serverinlen;
756
757 if (initialContextToken) {
758 if ((text->plug.client->features & SASL_FEAT_GSS_FRAMING) == 0)
759 text->gs2_flags |= GS2_NONSTD_FLAG;
760
761 switch (params->cbindingdisp) {
762 case SASL_CB_DISP_NONE:
763 text->gs2_flags |= GS2_CB_FLAG_N;
764 break;
765 case SASL_CB_DISP_USED:
766 text->gs2_flags |= GS2_CB_FLAG_P;
767 break;
768 case SASL_CB_DISP_WANT:
769 text->gs2_flags |= GS2_CB_FLAG_Y;
770 break;
771 }
772
773 ret = gs2_make_header(text, params,
774 strcmp(oparams->user, oparams->authid) ?
775 (char *) oparams->user : NULL,
776 &text->out_buf, &text->out_buf_len);
777 if (ret != 0) {
778 goto cleanup;
779 }
780 }
781
782 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
783
784 maj_stat = gss_init_sec_context(&min_stat,
785 (params->gss_creds != GSS_C_NO_CREDENTIAL)
786 ? (gss_cred_id_t)params->gss_creds
787 : text->client_creds,
788 &text->gss_ctx,
789 text->server_name,
790 (gss_OID)text->mechanism,
791 req_flags,
792 GSS_C_INDEFINITE,
793 &text->gss_cbindings,
794 serverinlen ? &input_token : GSS_C_NO_BUFFER,
795 NULL,
796 &output_token,
797 &ret_flags,
798 &text->lifetime);
799 if (GSS_ERROR(maj_stat)) {
800 ret = SASL_OK;
801 goto cleanup;
802 }
803
804 ret = gs2_make_message(text, params, initialContextToken, &output_token,
805 &text->out_buf, &text->out_buf_len);
806 if (ret != 0) {
807 goto cleanup;
808 }
809
810 *clientout = text->out_buf;
811 *clientoutlen = text->out_buf_len;
812
813 if (maj_stat == GSS_S_CONTINUE_NEEDED) {
814 ret = SASL_CONTINUE;
815 goto cleanup;
816 }
817
818 if (text->client_name != GSS_C_NO_NAME) {
819 gss_release_name(&min_stat, &text->client_name);
820 }
821 maj_stat = gss_inquire_context(&min_stat,
822 text->gss_ctx,
823 &text->client_name,
824 NULL,
825 &text->lifetime,
826 NULL,
827 &ret_flags, /* flags */
828 NULL,
829 NULL);
830 if (GSS_ERROR(maj_stat)) {
831 ret = SASL_OK;
832 goto cleanup;
833 }
834
835 if ((ret_flags & req_flags) != req_flags) {
836 ret = SASL_BADAUTH;
837 goto cleanup;
838 }
839
840 maj_stat = gss_display_name(&min_stat,
841 text->client_name,
842 &name_buf,
843 NULL);
844 if (GSS_ERROR(maj_stat)) {
845 ret = SASL_OK;
846 goto cleanup;
847 }
848
849 oparams->gss_peer_name = text->server_name;
850 oparams->gss_local_name = text->client_name;
851 oparams->encode = NULL;
852 oparams->decode = NULL;
853 oparams->mech_ssf = 0;
854 oparams->maxoutbuf = 0xFFFFFF;
855 oparams->doneflag = 1;
856
857 ret = SASL_OK;
858
859 cleanup:
860 if (ret == SASL_OK && maj_stat != GSS_S_COMPLETE) {
861 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
862 ret = SASL_FAIL;
863 }
864
865 gss_release_buffer(&min_stat, &output_token);
866 gss_release_buffer(&min_stat, &name_buf);
867
868 if (ret < SASL_OK) {
869 sasl_gs2_free_context_contents(text);
870 }
871
872 return ret;
873 }
874
gs2_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)875 static int gs2_client_mech_new(void *glob_context,
876 sasl_client_params_t *params,
877 void **conn_context)
878 {
879 context_t *text;
880 int ret;
881
882 text = sasl_gs2_new_context(params->utils);
883 if (text == NULL) {
884 MEMERROR(params->utils);
885 return SASL_NOMEM;
886 }
887
888 text->gss_ctx = GSS_C_NO_CONTEXT;
889 text->client_name = GSS_C_NO_NAME;
890 text->server_creds = GSS_C_NO_CREDENTIAL;
891 text->client_creds = GSS_C_NO_CREDENTIAL;
892 text->plug.client = glob_context;
893
894 ret = gs2_map_sasl_name(params->utils, text->plug.client->mech_name,
895 &text->mechanism);
896 if (ret != SASL_OK) {
897 gs2_common_mech_dispose(text, params->utils);
898 return ret;
899 }
900
901 *conn_context = text;
902
903 return SASL_OK;
904 }
905
906 static int
gs2_client_plug_alloc(const sasl_utils_t * utils,void * plug,gss_buffer_t sasl_name,gss_OID mech)907 gs2_client_plug_alloc(const sasl_utils_t *utils,
908 void *plug,
909 gss_buffer_t sasl_name,
910 gss_OID mech)
911 {
912 int ret;
913 sasl_client_plug_t *cplug = (sasl_client_plug_t *)plug;
914 gss_buffer_desc buf;
915
916 memset(cplug, 0, sizeof(*cplug));
917
918 ret = gs2_get_mech_attrs(utils, mech,
919 &cplug->security_flags,
920 &cplug->features,
921 &cplug->required_prompts);
922 if (ret != SASL_OK)
923 return ret;
924
925 ret = gs2_duplicate_buffer(utils, sasl_name, &buf);
926 if (ret != SASL_OK)
927 return ret;
928
929 cplug->mech_name = (char *)buf.value;
930 cplug->features |= SASL_FEAT_NEEDSERVERFQDN;
931 cplug->glob_context = plug;
932 cplug->mech_new = gs2_client_mech_new;
933 cplug->mech_step = gs2_client_mech_step;
934 cplug->mech_dispose = gs2_common_mech_dispose;
935 cplug->mech_free = gs2_common_mech_free;
936
937 return SASL_OK;
938 }
939
940 static sasl_client_plug_t *gs2_client_plugins;
941 static int gs2_client_plugcount;
942
943 int
gs2_client_plug_init(const sasl_utils_t * utils,int maxversion,int * outversion,sasl_client_plug_t ** pluglist,int * plugcount)944 gs2_client_plug_init(const sasl_utils_t *utils,
945 int maxversion,
946 int *outversion,
947 sasl_client_plug_t **pluglist,
948 int *plugcount)
949 {
950 int ret;
951
952 *pluglist = NULL;
953 *plugcount = 0;
954
955 if (maxversion < SASL_CLIENT_PLUG_VERSION)
956 return SASL_BADVERS;
957
958 *outversion = SASL_CLIENT_PLUG_VERSION;
959
960 if (gs2_client_plugins == NULL) {
961 ret = gs2_common_plug_init(utils,
962 sizeof(sasl_client_plug_t),
963 gs2_client_plug_alloc,
964 (void **)&gs2_client_plugins,
965 &gs2_client_plugcount);
966 if (ret != SASL_OK)
967 return ret;
968 }
969
970 *pluglist = gs2_client_plugins;
971 *plugcount = gs2_client_plugcount;
972
973 return SASL_OK;
974 }
975
976 /*
977 * Copy header and application channel bindings to GSS channel bindings
978 * structure in context.
979 */
980 static int
gs2_save_cbindings(context_t * text,gss_buffer_t header,const sasl_channel_binding_t * cbinding)981 gs2_save_cbindings(context_t *text,
982 gss_buffer_t header,
983 const sasl_channel_binding_t *cbinding)
984 {
985 gss_buffer_t gss_cbindings = &text->gss_cbindings.application_data;
986 size_t len;
987 unsigned char *p;
988
989 assert(gss_cbindings->value == NULL);
990
991 /*
992 * The application-data field MUST be set to the gs2-header, excluding
993 * the initial [gs2-nonstd-flag ","] part, concatenated with, when a
994 * gs2-cb-flag of "p" is used, the application's channel binding data.
995 */
996 len = header->length;
997 if (text->gs2_flags & GS2_NONSTD_FLAG) {
998 assert(len > 2);
999 len -= 2;
1000 }
1001 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
1002 cbinding != NULL) {
1003 len += cbinding->len;
1004 }
1005
1006 gss_cbindings->length = len;
1007 gss_cbindings->value = text->utils->malloc(len);
1008 if (gss_cbindings->value == NULL)
1009 return SASL_NOMEM;
1010
1011 p = (unsigned char *)gss_cbindings->value;
1012 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1013 memcpy(p, (unsigned char *)header->value + 2, header->length - 2);
1014 p += header->length - 2;
1015 } else {
1016 memcpy(p, header->value, header->length);
1017 p += header->length;
1018 }
1019
1020 if ((text->gs2_flags & GS2_CB_FLAG_MASK) == GS2_CB_FLAG_P &&
1021 cbinding != NULL) {
1022 memcpy(p, cbinding->data, cbinding->len);
1023 }
1024
1025 return SASL_OK;
1026 }
1027
1028 #define CHECK_REMAIN(n) do { if (remain < (n)) return SASL_BADPROT; } while (0)
1029
1030 /*
1031 * Verify gs2-header, save authzid and channel bindings to context.
1032 */
1033 static int
gs2_verify_initial_message(context_t * text,sasl_server_params_t * sparams,const char * in,unsigned inlen,gss_buffer_t token)1034 gs2_verify_initial_message(context_t *text,
1035 sasl_server_params_t *sparams,
1036 const char *in,
1037 unsigned inlen,
1038 gss_buffer_t token)
1039 {
1040 OM_uint32 major, minor;
1041 char *p = (char *)in;
1042 unsigned remain = inlen;
1043 int ret;
1044 gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
1045
1046 assert(text->cbindingname == NULL);
1047 assert(text->authzid == NULL);
1048
1049 token->length = 0;
1050 token->value = NULL;
1051
1052 /* minimum header includes CB flag and non-zero GSS token */
1053 CHECK_REMAIN(4); /* [pny],,. */
1054
1055 /* non-standard GSS framing flag */
1056 if (remain > 1 && memcmp(p, "F,", 2) == 0) {
1057 text->gs2_flags |= GS2_NONSTD_FLAG;
1058 remain -= 2;
1059 p += 2;
1060 }
1061
1062 /* SASL channel bindings */
1063 CHECK_REMAIN(1); /* [pny] */
1064 remain--;
1065 switch (*p++) {
1066 case 'p':
1067 CHECK_REMAIN(1); /* = */
1068 remain--;
1069 if (*p++ != '=')
1070 return SASL_BADPROT;
1071
1072 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->cbindingname);
1073 if (ret != SASL_OK)
1074 return ret;
1075
1076 text->gs2_flags |= GS2_CB_FLAG_P;
1077 break;
1078 case 'n':
1079 text->gs2_flags |= GS2_CB_FLAG_N;
1080 break;
1081 case 'y':
1082 text->gs2_flags |= GS2_CB_FLAG_Y;
1083 break;
1084 }
1085
1086 CHECK_REMAIN(1); /* , */
1087 remain--;
1088 if (*p++ != ',')
1089 return SASL_BADPROT;
1090
1091 /* authorization identity */
1092 if (remain > 1 && memcmp(p, "a=", 2) == 0) {
1093 CHECK_REMAIN(2);
1094 remain -= 2;
1095 p += 2;
1096
1097 ret = gs2_unescape_authzid(text->utils, &p, &remain, &text->authzid);
1098 if (ret != SASL_OK)
1099 return ret;
1100 }
1101
1102 /* end of header */
1103 CHECK_REMAIN(1); /* , */
1104 remain--;
1105 if (*p++ != ',')
1106 return SASL_BADPROT;
1107
1108 buf.length = inlen - remain;
1109 buf.value = (void *)in;
1110
1111 /* stash channel bindings to pass into gss_accept_sec_context() */
1112 ret = gs2_save_cbindings(text, &buf, sparams->cbinding);
1113 if (ret != SASL_OK)
1114 return ret;
1115
1116 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1117 buf.length = remain;
1118 buf.value = p;
1119 } else {
1120 gss_buffer_desc tmp;
1121
1122 tmp.length = remain;
1123 tmp.value = p;
1124
1125 major = gss_encapsulate_token(&tmp, text->mechanism, &buf);
1126 if (GSS_ERROR(major))
1127 return SASL_NOMEM;
1128 }
1129
1130 token->value = text->utils->malloc(buf.length);
1131 if (token->value == NULL)
1132 return SASL_NOMEM;
1133
1134 token->length = buf.length;
1135 memcpy(token->value, buf.value, buf.length);
1136
1137 if ((text->gs2_flags & GS2_NONSTD_FLAG) == 0)
1138 gss_release_buffer(&minor, &buf);
1139
1140 return SASL_OK;
1141 }
1142
1143 /*
1144 * Create gs2-header, save channel bindings to context.
1145 */
1146 static int
gs2_make_header(context_t * text,sasl_client_params_t * cparams,const char * authzid,char ** out,unsigned * outlen)1147 gs2_make_header(context_t *text,
1148 sasl_client_params_t *cparams,
1149 const char *authzid,
1150 char **out,
1151 unsigned *outlen)
1152 {
1153 size_t required = 0;
1154 size_t wire_authzid_len = 0, cbnamelen = 0;
1155 char *wire_authzid = NULL;
1156 char *p;
1157 int ret;
1158 gss_buffer_desc buf;
1159
1160 *out = NULL;
1161 *outlen = 0;
1162
1163 /* non-standard GSS framing flag */
1164 if (text->gs2_flags & GS2_NONSTD_FLAG)
1165 required += 2; /* F, */
1166
1167 /* SASL channel bindings */
1168 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1169 case GS2_CB_FLAG_P:
1170 if (!SASL_CB_PRESENT(cparams))
1171 return SASL_BADPARAM;
1172 cbnamelen = strlen(cparams->cbinding->name);
1173 required += 1 /*=*/ + cbnamelen;
1174 /* fallthrough */
1175 case GS2_CB_FLAG_N:
1176 case GS2_CB_FLAG_Y:
1177 required += 2; /* [pny], */
1178 break;
1179 default:
1180 return SASL_BADPARAM;
1181 }
1182
1183 /* authorization identity */
1184 if (authzid != NULL) {
1185 ret = gs2_escape_authzid(text->utils, authzid,
1186 strlen(authzid), &wire_authzid);
1187 if (ret != SASL_OK)
1188 return ret;
1189
1190 wire_authzid_len = strlen(wire_authzid);
1191 required += 2 /* a= */ + wire_authzid_len;
1192 }
1193
1194 required += 1; /* trailing comma */
1195
1196 ret = _plug_buf_alloc(text->utils, out, outlen, required);
1197 if (ret != SASL_OK) {
1198 text->utils->free(wire_authzid);
1199 return ret;
1200 }
1201
1202 *out = text->out_buf;
1203 *outlen = required;
1204
1205 p = (char *)text->out_buf;
1206 if (text->gs2_flags & GS2_NONSTD_FLAG) {
1207 *p++ = 'F';
1208 *p++ = ',';
1209 }
1210 switch (text->gs2_flags & GS2_CB_FLAG_MASK) {
1211 case GS2_CB_FLAG_P:
1212 memcpy(p, "p=", 2);
1213 memcpy(p + 2, cparams->cbinding->name, cbnamelen);
1214 p += 2 + cbnamelen;
1215 break;
1216 case GS2_CB_FLAG_N:
1217 *p++ = 'n';
1218 break;
1219 case GS2_CB_FLAG_Y:
1220 *p++ = 'y';
1221 break;
1222 }
1223 *p++ = ',';
1224 if (wire_authzid != NULL) {
1225 memcpy(p, "a=", 2);
1226 memcpy(p + 2, wire_authzid, wire_authzid_len);
1227 text->utils->free(wire_authzid);
1228 p += 2 + wire_authzid_len;
1229 }
1230 *p++ = ',';
1231
1232 assert(p == (char *)text->out_buf + required);
1233
1234 buf.length = required;
1235 buf.value = *out;
1236
1237 ret = gs2_save_cbindings(text, &buf, cparams->cbinding);
1238 if (ret != SASL_OK)
1239 return ret;
1240
1241 return SASL_OK;
1242 }
1243
1244 /*
1245 * Convert a GSS token to a GS2 one
1246 */
1247 static int
gs2_make_message(context_t * text,sasl_client_params_t * cparams,int initialContextToken,gss_buffer_t token,char ** out,unsigned * outlen)1248 gs2_make_message(context_t *text,
1249 sasl_client_params_t *cparams __attribute__((unused)),
1250 int initialContextToken,
1251 gss_buffer_t token,
1252 char **out,
1253 unsigned *outlen)
1254 {
1255 OM_uint32 major, minor;
1256 int ret;
1257 unsigned header_len = 0;
1258 gss_buffer_desc decap_token = GSS_C_EMPTY_BUFFER;
1259
1260 if (initialContextToken) {
1261 header_len = *outlen;
1262
1263 major = gss_decapsulate_token(token, text->mechanism, &decap_token);
1264 if ((major == GSS_S_DEFECTIVE_TOKEN &&
1265 (text->plug.client->features & SASL_FEAT_GSS_FRAMING)) ||
1266 GSS_ERROR(major))
1267 return SASL_FAIL;
1268
1269 token = &decap_token;
1270 }
1271
1272 ret = _plug_buf_alloc(text->utils, out, outlen,
1273 header_len + token->length);
1274 if (ret != 0)
1275 return ret;
1276
1277 memcpy(*out + header_len, token->value, token->length);
1278 *outlen = header_len + token->length;
1279
1280 if (initialContextToken)
1281 gss_release_buffer(&minor, &decap_token);
1282
1283 return SASL_OK;
1284 }
1285
1286 static const unsigned long gs2_required_prompts[] = {
1287 SASL_CB_LIST_END
1288 };
1289
1290 /*
1291 * Map GSS mechanism attributes to SASL ones
1292 */
1293 static int
gs2_get_mech_attrs(const sasl_utils_t * utils,const gss_OID mech,unsigned int * security_flags,unsigned int * features,const unsigned long ** prompts)1294 gs2_get_mech_attrs(const sasl_utils_t *utils,
1295 const gss_OID mech,
1296 unsigned int *security_flags,
1297 unsigned int *features,
1298 const unsigned long **prompts)
1299 {
1300 OM_uint32 major, minor;
1301 int present;
1302 gss_OID_set attrs = GSS_C_NO_OID_SET;
1303
1304 major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
1305 if (GSS_ERROR(major)) {
1306 utils->seterror(utils->conn, SASL_NOLOG,
1307 "GS2 Failure: gss_inquire_attrs_for_mech");
1308 return SASL_FAIL;
1309 }
1310
1311 *security_flags = SASL_SEC_NOPLAINTEXT | SASL_SEC_NOACTIVE;
1312 *features = SASL_FEAT_WANT_CLIENT_FIRST | SASL_FEAT_CHANNEL_BINDING;
1313 if (prompts != NULL)
1314 *prompts = gs2_required_prompts;
1315
1316 #define MA_PRESENT(a) (gss_test_oid_set_member(&minor, (gss_OID)(a), \
1317 attrs, &present) == GSS_S_COMPLETE && \
1318 present)
1319
1320 if (MA_PRESENT(GSS_C_MA_PFS))
1321 *security_flags |= SASL_SEC_FORWARD_SECRECY;
1322 if (!MA_PRESENT(GSS_C_MA_AUTH_INIT_ANON))
1323 *security_flags |= SASL_SEC_NOANONYMOUS;
1324 if (MA_PRESENT(GSS_C_MA_DELEG_CRED))
1325 *security_flags |= SASL_SEC_PASS_CREDENTIALS;
1326 if (MA_PRESENT(GSS_C_MA_AUTH_TARG))
1327 *security_flags |= SASL_SEC_MUTUAL_AUTH;
1328 if (MA_PRESENT(GSS_C_MA_AUTH_INIT_INIT) && prompts != NULL)
1329 *prompts = NULL;
1330 if (MA_PRESENT(GSS_C_MA_ITOK_FRAMED))
1331 *features |= SASL_FEAT_GSS_FRAMING;
1332
1333 gss_release_oid_set(&minor, &attrs);
1334
1335 return SASL_OK;
1336 }
1337
1338 /*
1339 * Enumerate GSS mechanisms that can be used for GS2
1340 */
gs2_indicate_mechs(const sasl_utils_t * utils)1341 static int gs2_indicate_mechs(const sasl_utils_t *utils)
1342 {
1343 OM_uint32 major, minor;
1344 gss_OID_desc desired_oids[3];
1345 gss_OID_set_desc desired_attrs;
1346 gss_OID_desc except_oids[3];
1347 gss_OID_set_desc except_attrs;
1348
1349 if (gs2_mechs != GSS_C_NO_OID_SET)
1350 return SASL_OK;
1351
1352 desired_oids[0] = *GSS_C_MA_AUTH_INIT;
1353 desired_oids[1] = *GSS_C_MA_AUTH_TARG;
1354 desired_oids[2] = *GSS_C_MA_CBINDINGS;
1355 desired_attrs.count = sizeof(desired_oids)/sizeof(desired_oids[0]);
1356 desired_attrs.elements = desired_oids;
1357
1358 except_oids[0] = *GSS_C_MA_MECH_NEGO;
1359 except_oids[1] = *GSS_C_MA_NOT_MECH;
1360 except_oids[2] = *GSS_C_MA_DEPRECATED;
1361
1362 except_attrs.count = sizeof(except_oids)/sizeof(except_oids[0]);
1363 except_attrs.elements = except_oids;
1364
1365 major = gss_indicate_mechs_by_attrs(&minor,
1366 &desired_attrs,
1367 &except_attrs,
1368 GSS_C_NO_OID_SET,
1369 &gs2_mechs);
1370 if (GSS_ERROR(major)) {
1371 utils->seterror(utils->conn, SASL_NOLOG,
1372 "GS2 Failure: gss_indicate_mechs_by_attrs");
1373 return SASL_FAIL;
1374 }
1375
1376 return (gs2_mechs->count > 0) ? SASL_OK : SASL_NOMECH;
1377 }
1378
1379 /*
1380 * Map SASL mechanism name to OID
1381 */
1382 static int
gs2_map_sasl_name(const sasl_utils_t * utils,const char * mech,gss_OID * oid)1383 gs2_map_sasl_name(const sasl_utils_t *utils,
1384 const char *mech,
1385 gss_OID *oid)
1386 {
1387 OM_uint32 major, minor;
1388 gss_buffer_desc buf;
1389
1390 buf.length = strlen(mech);
1391 buf.value = (void *)mech;
1392
1393 major = gss_inquire_mech_for_saslname(&minor, &buf, oid);
1394 if (GSS_ERROR(major)) {
1395 utils->seterror(utils->conn, SASL_NOLOG,
1396 "GS2 Failure: gss_inquire_mech_for_saslname");
1397 return SASL_FAIL;
1398 }
1399
1400 return SASL_OK;
1401 }
1402
1403 static int
gs2_duplicate_buffer(const sasl_utils_t * utils,const gss_buffer_t src,gss_buffer_t dst)1404 gs2_duplicate_buffer(const sasl_utils_t *utils,
1405 const gss_buffer_t src,
1406 gss_buffer_t dst)
1407 {
1408 dst->value = utils->malloc(src->length + 1);
1409 if (dst->value == NULL)
1410 return SASL_NOMEM;
1411
1412 memcpy(dst->value, src->value, src->length);
1413 ((char *)dst->value)[src->length] = '\0';
1414 dst->length = src->length;
1415
1416 return SASL_OK;
1417 }
1418
1419 static int
gs2_unescape_authzid(const sasl_utils_t * utils,char ** endp,unsigned * remain,char ** authzid)1420 gs2_unescape_authzid(const sasl_utils_t *utils,
1421 char **endp,
1422 unsigned *remain,
1423 char **authzid)
1424 {
1425 char *in = *endp;
1426 size_t i, len, inlen = *remain;
1427 char *p;
1428
1429 *endp = NULL;
1430
1431 for (i = 0, len = 0; i < inlen; i++) {
1432 if (in[i] == ',') {
1433 *endp = &in[i];
1434 *remain -= i;
1435 break;
1436 } else if (in[i] == '=') {
1437 if (inlen <= i + 2)
1438 return SASL_BADPROT;
1439 i += 2;
1440 }
1441 len++;
1442 }
1443
1444 if (len == 0 || *endp == NULL)
1445 return SASL_BADPROT;
1446
1447 p = *authzid = utils->malloc(len + 1);
1448 if (*authzid == NULL)
1449 return SASL_NOMEM;
1450
1451 for (i = 0; i < inlen; i++) {
1452 if (in[i] == ',')
1453 break;
1454 else if (in[i] == '=') {
1455 if (memcmp(&in[i + 1], "2C", 2) == 0)
1456 *p++ = ',';
1457 else if (memcmp(&in[i + 1], "3D", 2) == 0)
1458 *p++ = '=';
1459 else {
1460 utils->free(*authzid);
1461 *authzid = NULL;
1462 return SASL_BADPROT;
1463 }
1464 i += 2;
1465 } else
1466 *p++ = in[i];
1467 }
1468
1469 *p = '\0';
1470
1471 return SASL_OK;
1472 }
1473
1474 static int
gs2_escape_authzid(const sasl_utils_t * utils,const char * in,unsigned inlen,char ** authzid)1475 gs2_escape_authzid(const sasl_utils_t *utils,
1476 const char *in,
1477 unsigned inlen,
1478 char **authzid)
1479 {
1480 size_t i;
1481 char *p;
1482
1483 p = *authzid = utils->malloc((inlen * 3) + 1);
1484 if (*authzid == NULL)
1485 return SASL_NOMEM;
1486
1487 for (i = 0; i < inlen; i++) {
1488 if (in[i] == ',') {
1489 memcpy(p, "=2C", 3);
1490 p += 3;
1491 } else if (in[i] == '=') {
1492 memcpy(p, "=3D", 3);
1493 p += 3;
1494 } else {
1495 *p++ = in[i];
1496 }
1497 }
1498
1499 *p = '\0';
1500
1501 return SASL_OK;
1502 }
1503
1504 #define GOT_CREDS(text, params) ((text)->client_creds != NULL || (params)->gss_creds != NULL)
1505 #define CRED_ERROR(status) ((status) == GSS_S_CRED_UNAVAIL || (status) == GSS_S_NO_CRED)
1506
1507 /*
1508 * Determine the authentication identity from the application supplied
1509 * GSS credential, the application supplied identity, and the default
1510 * GSS credential, in that order. Then, acquire credentials.
1511 */
1512 static int
gs2_get_init_creds(context_t * text,sasl_client_params_t * params,sasl_interact_t ** prompt_need,sasl_out_params_t * oparams)1513 gs2_get_init_creds(context_t *text,
1514 sasl_client_params_t *params,
1515 sasl_interact_t **prompt_need,
1516 sasl_out_params_t *oparams)
1517 {
1518 int result = SASL_OK;
1519 const char *authid = NULL, *userid = NULL;
1520 int user_result = SASL_OK;
1521 int auth_result = SASL_OK;
1522 int pass_result = SASL_OK;
1523 OM_uint32 maj_stat = GSS_S_COMPLETE, min_stat = 0;
1524 gss_OID_set_desc mechs;
1525 gss_buffer_desc cred_authid = GSS_C_EMPTY_BUFFER;
1526 gss_buffer_desc name_buf = GSS_C_EMPTY_BUFFER;
1527
1528 mechs.count = 1;
1529 mechs.elements = (gss_OID)text->mechanism;
1530
1531 /*
1532 * Get the authentication identity from the application.
1533 */
1534 if (oparams->authid == NULL) {
1535 auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1536 if (auth_result != SASL_OK && auth_result != SASL_INTERACT) {
1537 result = auth_result;
1538 goto cleanup;
1539 }
1540 }
1541
1542 /*
1543 * Get the authorization identity from the application.
1544 */
1545 if (oparams->user == NULL) {
1546 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
1547 if (user_result != SASL_OK && user_result != SASL_INTERACT) {
1548 result = user_result;
1549 goto cleanup;
1550 }
1551 }
1552
1553 /*
1554 * Canonicalize the authentication and authorization identities before
1555 * calling GSS_Import_name.
1556 */
1557 if (auth_result == SASL_OK && user_result == SASL_OK &&
1558 oparams->authid == NULL) {
1559 if (userid == NULL || userid[0] == '\0') {
1560 result = params->canon_user(params->utils->conn, authid, 0,
1561 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1562 oparams);
1563 } else {
1564 result = params->canon_user(params->utils->conn,
1565 authid, 0, SASL_CU_AUTHID, oparams);
1566 if (result != SASL_OK)
1567 goto cleanup;
1568
1569 result = params->canon_user(params->utils->conn,
1570 userid, 0, SASL_CU_AUTHZID, oparams);
1571 if (result != SASL_OK)
1572 goto cleanup;
1573 }
1574
1575 if (oparams->authid != NULL) {
1576 name_buf.length = strlen(oparams->authid);
1577 name_buf.value = (void *)oparams->authid;
1578
1579 assert(text->client_name == GSS_C_NO_NAME);
1580
1581 maj_stat = gss_import_name(&min_stat,
1582 &name_buf,
1583 GSS_C_NT_USER_NAME,
1584 &text->client_name);
1585 if (GSS_ERROR(maj_stat))
1586 goto cleanup;
1587 }
1588 }
1589
1590 /*
1591 * If application didn't provide an authid, then use the default
1592 * credential. If that doesn't work, give up.
1593 */
1594 if (!GOT_CREDS(text, params) && oparams->authid == NULL) {
1595 maj_stat = gss_acquire_cred(&min_stat,
1596 GSS_C_NO_NAME,
1597 GSS_C_INDEFINITE,
1598 &mechs,
1599 GSS_C_INITIATE,
1600 &text->client_creds,
1601 NULL,
1602 &text->lifetime);
1603 if (GSS_ERROR(maj_stat))
1604 goto cleanup;
1605
1606 assert(text->client_name == GSS_C_NO_NAME);
1607
1608 maj_stat = gss_inquire_cred(&min_stat,
1609 params->gss_creds
1610 ? (gss_cred_id_t)params->gss_creds
1611 : text->client_creds,
1612 &text->client_name,
1613 NULL,
1614 NULL,
1615 NULL);
1616 if (GSS_ERROR(maj_stat))
1617 goto cleanup;
1618
1619 maj_stat = gss_display_name(&min_stat,
1620 text->client_name,
1621 &cred_authid,
1622 NULL);
1623 if (GSS_ERROR(maj_stat))
1624 goto cleanup;
1625
1626 if (userid == NULL || userid[0] == '\0') {
1627 result = params->canon_user(params->utils->conn,
1628 cred_authid.value, cred_authid.length,
1629 SASL_CU_AUTHID | SASL_CU_AUTHZID,
1630 oparams);
1631 } else {
1632 result = params->canon_user(params->utils->conn,
1633 cred_authid.value, cred_authid.length,
1634 SASL_CU_AUTHID, oparams);
1635 if (result != SASL_OK)
1636 goto cleanup;
1637
1638 result = params->canon_user(params->utils->conn,
1639 cred_authid.value, cred_authid.length,
1640 SASL_CU_AUTHZID, oparams);
1641 if (result != SASL_OK)
1642 goto cleanup;
1643 }
1644 }
1645
1646 /*
1647 * Armed with the authentication identity, try to get a credential without
1648 * a password.
1649 */
1650 if (!GOT_CREDS(text, params) && text->client_name != GSS_C_NO_NAME) {
1651 maj_stat = gss_acquire_cred(&min_stat,
1652 text->client_name,
1653 GSS_C_INDEFINITE,
1654 &mechs,
1655 GSS_C_INITIATE,
1656 &text->client_creds,
1657 NULL,
1658 &text->lifetime);
1659 if (GSS_ERROR(maj_stat) && !CRED_ERROR(maj_stat))
1660 goto cleanup;
1661 }
1662
1663 /*
1664 * If that failed, try to get a credential with a password.
1665 */
1666 if (!GOT_CREDS(text, params)) {
1667 if (text->password == NULL) {
1668 pass_result = _plug_get_password(params->utils, &text->password,
1669 &text->free_password, prompt_need);
1670 if (pass_result != SASL_OK && pass_result != SASL_INTERACT) {
1671 result = pass_result;
1672 goto cleanup;
1673 }
1674 }
1675
1676 if (text->password != NULL) {
1677 gss_buffer_desc password_buf;
1678
1679 password_buf.length = text->password->len;
1680 password_buf.value = text->password->data;
1681
1682 maj_stat = gss_acquire_cred_with_password(&min_stat,
1683 text->client_name,
1684 &password_buf,
1685 GSS_C_INDEFINITE,
1686 &mechs,
1687 GSS_C_INITIATE,
1688 &text->client_creds,
1689 NULL,
1690 &text->lifetime);
1691 if (GSS_ERROR(maj_stat))
1692 goto cleanup;
1693 }
1694 }
1695
1696 maj_stat = GSS_S_COMPLETE;
1697
1698 /* free prompts we got */
1699 if (prompt_need && *prompt_need) {
1700 params->utils->free(*prompt_need);
1701 *prompt_need = NULL;
1702 }
1703
1704 /* if there are prompts not filled in */
1705 if (user_result == SASL_INTERACT || auth_result == SASL_INTERACT ||
1706 pass_result == SASL_INTERACT) {
1707 /* make the prompt list */
1708 result =
1709 _plug_make_prompts(params->utils, prompt_need,
1710 user_result == SASL_INTERACT ?
1711 "Please enter your authorization name" : NULL,
1712 NULL,
1713 auth_result == SASL_INTERACT ?
1714 "Please enter your authentication name" : NULL,
1715 NULL,
1716 pass_result == SASL_INTERACT ?
1717 "Please enter your password" : NULL, NULL,
1718 NULL, NULL, NULL,
1719 NULL,
1720 NULL, NULL);
1721 if (result == SASL_OK)
1722 result = SASL_INTERACT;
1723 }
1724
1725 cleanup:
1726 if (result == SASL_OK && maj_stat != GSS_S_COMPLETE) {
1727 sasl_gs2_seterror(text->utils, maj_stat, min_stat);
1728 result = SASL_FAIL;
1729 }
1730
1731 gss_release_buffer(&min_stat, &cred_authid);
1732
1733 return result;
1734 }
1735
1736 static int
sasl_gs2_seterror_(const sasl_utils_t * utils,OM_uint32 maj,OM_uint32 min,int logonly)1737 sasl_gs2_seterror_(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min,
1738 int logonly)
1739 {
1740 OM_uint32 maj_stat, min_stat;
1741 gss_buffer_desc msg;
1742 OM_uint32 msg_ctx;
1743 int ret;
1744 char *out = NULL;
1745 unsigned int len, curlen = 0;
1746 const char prefix[] = "GS2 Error: ";
1747
1748 len = sizeof(prefix);
1749 ret = _plug_buf_alloc(utils, &out, &curlen, 256);
1750 if (ret != SASL_OK)
1751 return SASL_OK;
1752
1753 strcpy(out, prefix);
1754
1755 msg_ctx = 0;
1756 while (1) {
1757 maj_stat = gss_display_status(&min_stat, maj,
1758 GSS_C_GSS_CODE, GSS_C_NULL_OID,
1759 &msg_ctx, &msg);
1760
1761 if (GSS_ERROR(maj_stat)) {
1762 if (logonly) {
1763 utils->log(utils->conn, SASL_LOG_FAIL,
1764 "GS2 Failure: (could not get major error message)");
1765 } else {
1766 utils->seterror(utils->conn, 0,
1767 "GS2 Failure "
1768 "(could not get major error message)");
1769 }
1770 utils->free(out);
1771 return SASL_OK;
1772 }
1773
1774 len += len + msg.length;
1775 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1776 if (ret != SASL_OK) {
1777 utils->free(out);
1778 return SASL_OK;
1779 }
1780
1781 strcat(out, msg.value);
1782
1783 gss_release_buffer(&min_stat, &msg);
1784
1785 if (!msg_ctx)
1786 break;
1787 }
1788
1789 /* Now get the minor status */
1790
1791 len += 2;
1792 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1793 if (ret != SASL_OK) {
1794 utils->free(out);
1795 return SASL_NOMEM;
1796 }
1797
1798 strcat(out, " (");
1799
1800 msg_ctx = 0;
1801 while (1) {
1802 maj_stat = gss_display_status(&min_stat, min,
1803 GSS_C_MECH_CODE, GSS_C_NULL_OID,
1804 &msg_ctx, &msg);
1805
1806 if (GSS_ERROR(maj_stat)) {
1807 if (logonly) {
1808 utils->log(utils->conn, SASL_LOG_FAIL,
1809 "GS2 Failure: (could not get minor error message)");
1810 } else {
1811 utils->seterror(utils->conn, 0,
1812 "GS2 Failure "
1813 "(could not get minor error message)");
1814 }
1815 utils->free(out);
1816 return SASL_OK;
1817 }
1818
1819 len += len + msg.length;
1820
1821 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1822 if (ret != SASL_OK) {
1823 utils->free(out);
1824 return SASL_NOMEM;
1825 }
1826
1827 strcat(out, msg.value);
1828
1829 gss_release_buffer(&min_stat, &msg);
1830
1831 if (!msg_ctx)
1832 break;
1833 }
1834
1835 len += 1;
1836 ret = _plug_buf_alloc(utils, &out, &curlen, len);
1837 if (ret != SASL_OK) {
1838 utils->free(out);
1839 return SASL_NOMEM;
1840 }
1841
1842 strcat(out, ")");
1843
1844 if (logonly) {
1845 utils->log(utils->conn, SASL_LOG_FAIL, "%s", out);
1846 } else {
1847 utils->seterror(utils->conn, 0, "%s", out);
1848 }
1849 utils->free(out);
1850
1851 return SASL_OK;
1852 }
1853