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