From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- third_party/heimdal/lib/gssapi/gss-token.c | 684 +++++++++++++++++++++++++++++ 1 file changed, 684 insertions(+) create mode 100644 third_party/heimdal/lib/gssapi/gss-token.c (limited to 'third_party/heimdal/lib/gssapi/gss-token.c') diff --git a/third_party/heimdal/lib/gssapi/gss-token.c b/third_party/heimdal/lib/gssapi/gss-token.c new file mode 100644 index 0000000..a4c0296 --- /dev/null +++ b/third_party/heimdal/lib/gssapi/gss-token.c @@ -0,0 +1,684 @@ +/* */ + +/*- + * Copyright (c) 1997-2011 Roland C. Dowdeswell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer and + * dedication in the documentation and/or other materials provided + * with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#ifdef __APPLE__ +#include +#elif HAVE_MALLOC_H +#include +#endif +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define GBAIL(x, _maj, _min) do { \ + if (GSS_ERROR(_maj)) { \ + char *the_gss_err; \ + \ + ret = 1; \ + the_gss_err = gss_mk_err(_maj, _min, x); \ + if (the_gss_err) \ + fprintf(stderr, "%s\n", the_gss_err); \ + else \ + fprintf(stderr, "err making err\n"); \ + free(the_gss_err); \ + goto bail; \ + } \ + } while (0) + +#define K5BAIL(x) do { \ + kret = x; \ + if (kret) { \ + const char *k5err; \ + \ + k5err = krb5_get_error_message(kctx, kret); \ + if (k5err) { \ + fprintf(stderr, "%s in %s:%s\n", k5err, \ + #x, __func__); \ + krb5_free_error_message(kctx, k5err); \ + } else { \ + fprintf(stderr, "unknown error %d in " \ + "%s:%s\n", kret, #x, __func__); \ + } \ + exit(1); /* XXXrcd: shouldn't exit */ \ + } \ + } while (0) + + +/* + * global variables + */ + +int Sflag = 0; +int nflag = 0; +gss_OID global_mech = GSS_C_NO_OID; + +static char * +gss_mk_err(OM_uint32 maj_stat, OM_uint32 min_stat, const char *preamble) +{ + gss_buffer_desc status; + OM_uint32 new_stat; + OM_uint32 cur_stat; + OM_uint32 msg_ctx = 0; + OM_uint32 ret; + int type; + size_t newlen; + char *str = NULL; + char *tmp = NULL; + + cur_stat = maj_stat; + type = GSS_C_GSS_CODE; + + for (;;) { + + /* + * GSS_S_FAILURE produces a rather unhelpful message, so + * we skip straight to the mech specific error in this case. + */ + + if (type == GSS_C_GSS_CODE && cur_stat == GSS_S_FAILURE) { + type = GSS_C_MECH_CODE; + cur_stat = min_stat; + } + + ret = gss_display_status(&new_stat, cur_stat, type, + GSS_C_NO_OID, &msg_ctx, &status); + + if (GSS_ERROR(ret)) + return str; /* XXXrcd: hmmm, not quite?? */ + + if (str) + newlen = strlen(str); + else + newlen = strlen(preamble); + + newlen += status.length + 3; + + tmp = str; + str = malloc(newlen); + + if (!str) { + gss_release_buffer(&new_stat, &status); + return tmp; /* XXXrcd: hmmm, not quite?? */ + } + + snprintf(str, newlen, "%s%s%.*s", tmp?tmp:preamble, + tmp?", ":": ", (int)status.length, (char *)status.value); + + gss_release_buffer(&new_stat, &status); + free(tmp); + + /* + * If we are finished processing for maj_stat, then + * move onto min_stat. + */ + + if (msg_ctx == 0 && type == GSS_C_GSS_CODE && min_stat != 0) { + type = GSS_C_MECH_CODE; + cur_stat = min_stat; + continue; + } + + if (msg_ctx == 0) + break; + } + + return str; +} + +static char * +read_buffer(FILE *fp) +{ + char buf[65536]; + char *p; + char *ret = NULL; + size_t buflen; + size_t retlen = 0; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if ((p = strchr(buf, '\n')) == NULL) { + fprintf(stderr, "Long line, exiting.\n"); + exit(1); + } + *p = '\0'; + buflen = strlen(buf); + if (buflen == 0) + break; + + ret = realloc(ret, retlen + buflen + 1); + if (!ret) { + perror("realloc"); + exit(1); + } + memcpy(ret + retlen, buf, buflen); + ret[retlen + buflen] = '\0'; + retlen += buflen; + } + + if (ferror(stdin)) { + perror("fgets"); + exit(1); + } + + return ret; +} + +static int +write_and_free_token(gss_buffer_t out, int negotiate) +{ + OM_uint32 min; + char *outstr = NULL; + char *p = out->value; + size_t len = out->length; + size_t inc; + int ret = 0; + int first = 1; + + if (nflag) + goto bail; + + /* + * According to RFC 2744 page 25, we simply don't output + * zero length output tokens. + */ + if (len == 0) + goto bail; + + inc = len; + if (Sflag) + inc = Sflag; + + do { + if (first) + first = 0; + else + printf("\n"); + if (len < inc) + inc = len; + if (rk_base64_encode(p, inc, &outstr) < 0) { + fprintf(stderr, "Out of memory.\n"); + ret = errno; + goto bail; + } + ret = 0; + printf("%s%s\n", negotiate?"Negotiate ":"", outstr); + free(outstr); + p += inc; + len -= inc; + } while (len > 0); + ret = 0; + +bail: + gss_release_buffer(&min, out); + return ret; +} + +static int +read_token(gss_buffer_t in, int negotiate) +{ + char *inbuf = NULL; + char *tmp; + size_t len; + int ret = 0; + + /* We must flush before we block wanting input */ + fflush(stdout); + + *in = (gss_buffer_desc)GSS_C_EMPTY_BUFFER; + inbuf = read_buffer(stdin); + if (!inbuf) + /* Just a couple of \n's in a row or EOF, no error. */ + return 0; + + tmp = inbuf; + if (negotiate) { + if (strncasecmp("Negotiate ", inbuf, 10) != 0) { + fprintf(stderr, "Token doesn't begin with " + "\"Negotiate \"\n"); + ret = -1; + goto bail; + } + + tmp += 10; + } + + len = strlen(tmp); + in->value = malloc(len + 1); + if (!in->value) { + fprintf(stderr, "Out of memory.\n"); + ret = -1; + goto bail; + } + ret = rk_base64_decode(tmp, in->value); + if (ret < 0) { + free(in->value); + in->value = NULL; + if (errno == EOVERFLOW) + fprintf(stderr, "Token is too big\n"); + else + fprintf(stderr, "Token encoding is not valid " + "base64\n"); + goto bail; + } else { + in->length = ret; + } + ret = 0; + +bail: + free(inbuf); + return ret; +} + +static int +initiate_one(gss_name_t service, int delegate, int negotiate) +{ + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + gss_buffer_desc in; + gss_buffer_desc out; + OM_uint32 maj; + OM_uint32 min; + OM_uint32 flags = 0; + int first = 1; + int ret = 0; + + if (delegate) + flags |= GSS_C_DELEG_FLAG; + + do { + out.length = 0; + out.value = 0; + + if (first) { + in.length = 0; + in.value = 0; + first = 0; + } else { + printf("\n"); + ret = read_token(&in, negotiate); + if (ret) + return ret; + if (feof(stdin)) + return -1; + } + + maj = gss_init_sec_context(&min, GSS_C_NO_CREDENTIAL, &ctx, + service, global_mech, flags, 0, + GSS_C_NO_CHANNEL_BINDINGS, &in, NULL, &out, + NULL, NULL); + + ret = write_and_free_token(&out, negotiate); + if (ret) + return ret; + + GBAIL("gss_init_sec_context", maj, min); + } while (maj & GSS_S_CONTINUE_NEEDED); + +bail: + if (ctx != GSS_C_NO_CONTEXT) { + /* + * XXXrcd: here we ignore the fact that we might have an + * output token as this program doesn't do terribly + * well in that case. + */ + gss_delete_sec_context(&min, &ctx, NULL); + } + + return ret; +} + +static krb5_error_code +copy_cache(krb5_context kctx, krb5_ccache from, krb5_ccache to) +{ + krb5_error_code kret; + krb5_principal princ = NULL; + krb5_cc_cursor cursor; + krb5_creds cred; + + K5BAIL(krb5_cc_get_principal(kctx, from, &princ)); + K5BAIL(krb5_cc_initialize(kctx, to, princ)); + K5BAIL(krb5_cc_start_seq_get(kctx, from, &cursor)); + for (;;) { + kret = krb5_cc_next_cred(kctx, from, &cursor, &cred); + if (kret) + break; + kret = krb5_cc_store_cred(kctx, to, &cred); + krb5_free_cred_contents(kctx, &cred); + if (kret) + break; + } + krb5_cc_end_seq_get(kctx, from, &cursor); + + if (kret == KRB5_CC_END) + kret = 0; + K5BAIL(kret); + + if (princ) + krb5_free_principal(kctx, princ); + + return kret; +} + +static int +initiate_many(gss_name_t service, int delegate, int negotiate, int memcache, + size_t count) +{ + krb5_error_code kret = 0; + krb5_context kctx = NULL; + krb5_ccache def_cache = NULL; + krb5_ccache mem_cache = NULL; + size_t i; + + if (memcache) { + K5BAIL(krb5_init_context(&kctx)); + K5BAIL(krb5_cc_default(kctx, &def_cache)); + K5BAIL(krb5_cc_resolve(kctx, "MEMORY:mem_cache", &mem_cache)); + putenv("KRB5CCNAME=MEMORY:mem_cache"); + } + + for (i=0; i < count; i++) { + if (memcache) + K5BAIL(copy_cache(kctx, def_cache, mem_cache)); + kret = initiate_one(service, delegate, negotiate); + + if (!nflag && i < count - 1) + printf("\n"); + } + + if (kctx) + krb5_free_context(kctx); + if (def_cache) + krb5_cc_close(kctx, def_cache); + if (mem_cache) + krb5_cc_close(kctx, mem_cache); + + return kret; +} + +static int +accept_one(gss_name_t service, const char *ccname, int negotiate) +{ + gss_cred_id_t cred = NULL; + gss_cred_id_t deleg_creds = NULL; + gss_name_t client; + gss_OID mech_oid; + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + gss_buffer_desc in = GSS_C_EMPTY_BUFFER; + gss_buffer_desc out; + gss_buffer_desc dname = GSS_C_EMPTY_BUFFER; + krb5_context kctx = NULL; + krb5_ccache ccache = NULL; + krb5_error_code kret; + OM_uint32 maj, min; + int ret = 0; + + if (service) { + maj = gss_acquire_cred(&min, service, 0, NULL, GSS_C_ACCEPT, + &cred, NULL, NULL); + GBAIL("gss_acquire_cred", maj, min); + } + + do { + if (feof(stdin)) + return -1; + ret = read_token(&in, negotiate); + if (ret) + return ret; + + out.length = 0; + out.value = 0; + + maj = gss_accept_sec_context(&min, &ctx, cred, &in, + GSS_C_NO_CHANNEL_BINDINGS, &client, &mech_oid, &out, + NULL, NULL, &deleg_creds); + + ret = write_and_free_token(&out, negotiate); + if (ret) { + OM_uint32 junk; + + (void) gss_delete_sec_context(&junk, &ctx, + GSS_C_NO_BUFFER); + return ret; + } + GBAIL("gss_accept_sec_context", maj, min); + } while (maj & GSS_S_CONTINUE_NEEDED); + + /* + * XXXrcd: not bothering to clean up because we're about to exit. + * Probably should fix this in case the code is used as + * an example by someone. + */ + + maj = gss_display_name(&min, client, &dname, NULL); + GBAIL("gss_display_name", maj, min); + + if (!nflag) + printf("Authenticated: %.*s\n", (int)dname.length, + (char *)dname.value); + (void) gss_release_buffer(&min, &dname); + (void) gss_release_name(&min, &client); + (void) gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER); + + if (ccname) { +#ifdef HAVE_GSS_STORE_CRED_INTO + gss_key_value_set_desc store; + gss_key_value_element_desc elem; + int overwrite_cred = 1; + int default_cred = 0; + + elem.key = "ccache"; + elem.value = ccname; + store.count = 1; + store.elements = &elem; + + maj = gss_store_cred_into(&min, deleg_creds, GSS_C_INITIATE, + GSS_C_NO_OID, overwrite_cred, default_cred, &store, NULL, + NULL); + GBAIL("gss_store_cred_into", maj, min); +#else + K5BAIL(krb5_init_context(&kctx)); + K5BAIL(krb5_cc_resolve(kctx, ccname, &ccache)); + + maj = gss_krb5_copy_ccache(&min, deleg_creds, ccache); + GBAIL("gss_krb5_copy_ccache", maj, min); +#endif + } + +bail: + if (kctx) + krb5_free_context(kctx); + if (ccache) + krb5_cc_close(kctx, ccache); + if (cred) + gss_release_cred(&min, &cred); + if (deleg_creds) + gss_release_cred(&min, &deleg_creds); + + free(in.value); + + return ret; +} + +static gss_name_t +import_service(char *service) +{ + gss_buffer_desc name; + gss_name_t svc = NULL; + OM_uint32 maj; + OM_uint32 min; + int ret = 0; + + name.length = strlen(service); + name.value = service; + + maj = gss_import_name(&min, &name, GSS_C_NT_HOSTBASED_SERVICE, &svc); + + GBAIL("gss_import_name", maj, min); + +bail: + if (ret) + exit(1); + return svc; +} + +static void +print_all_mechs(void) +{ + OM_uint32 maj, min; + gss_OID_set mech_set; + size_t i; + int ret = 0; + + maj = gss_indicate_mechs(&min, &mech_set); + GBAIL("gss_indicate_mechs", maj, min); + + for (i=0; i < mech_set->count; i++) + printf("%s\n", gss_oid_to_name(&mech_set->elements[i])); + + (void) gss_release_oid_set(&min, &mech_set); + +bail: + exit(ret); +} + +static void +usage(int ecode) +{ + FILE *f = ecode == 0 ? stdout : stderr; + fprintf(f, "Usage: gss-token [-DNn] [-c count] service@host\n"); + fprintf(f, " gss-token -r [-Nln] [-C ccache] [-c count] " + "[service@host]\n"); + exit(ecode); +} + +int +main(int argc, char **argv) +{ + OM_uint32 min; + gss_name_t service = NULL; + size_t count = 1; + int Dflag = 0; + int Mflag = 0; + int Nflag = 0; + int hflag = 0; + int lflag = 0; + int rflag = 0; + int version_flag = 0; + int ret = 0; + int optidx = 0; + char *ccname = NULL; + char *mech = NULL; + struct getargs args[] = { + { "help", 'h', arg_flag, &hflag, NULL, NULL }, + { "version", 0, arg_flag, &version_flag, NULL, NULL }, + { NULL, 'C', arg_string, &ccname, NULL, NULL }, + { NULL, 'D', arg_flag, &Dflag, NULL, NULL }, + { NULL, 'M', arg_flag, &Mflag, NULL, NULL }, + { NULL, 'N', arg_flag, &Nflag, NULL, NULL }, + { NULL, 'S', arg_integer, &Sflag, NULL, NULL }, + { NULL, 'c', arg_integer, &count, NULL, NULL }, + { NULL, 'l', arg_flag, &lflag, NULL, NULL }, + { NULL, 'm', arg_string, &mech, NULL, NULL }, + { NULL, 'n', arg_flag, &nflag, NULL, NULL }, + { NULL, 'r', arg_flag, &rflag, NULL, NULL }, + }; + + setprogname(argv[0]); + if (argc == 1 || + getarg(args, sizeof(args)/sizeof(args[0]), argc, argv, &optidx)) + usage(1); + if (hflag) + usage(0); + if (version_flag) { + print_version(NULL); + return 0; + } + + argc -= optidx; + argv += optidx; + + if (mech) { + if (mech[0] == '?' && mech[1] == '\0') { + print_all_mechs(); + exit(0); + } + global_mech = gss_name_to_oid(mech); + if (!global_mech) { + fprintf(stderr, "Invalid mech \"%s\".\n", mech); + usage(1); + } + } + + if (argc > 0) + service = import_service(*argv); + + if (!rflag) { + if (!argc) { + fprintf(stderr, "Without -r, hostbased_service must " + "be provided.\n"); + usage(1); + } + if (ccname) { + fprintf(stderr, "Specifying a target ccache doesn't " + "make sense without -r.\n"); + usage(1); + } + ret = initiate_many(service, Dflag, Nflag, Mflag, count); + goto done; + } + + if (Dflag) { + fprintf(stderr, "Delegating credentials (-D) doesn't make " + "sense when reading tokens (-r).\n"); + usage(1); + } + + do { + ret = accept_one(service, ccname, Nflag); + } while (lflag && !ret && !feof(stdin)); + +done: + if (service) + gss_release_name(&min, &service); + + return ret; +} -- cgit v1.2.3