summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/gssapi/gss-token.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /third_party/heimdal/lib/gssapi/gss-token.c
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/heimdal/lib/gssapi/gss-token.c')
-rw-r--r--third_party/heimdal/lib/gssapi/gss-token.c684
1 files changed, 684 insertions, 0 deletions
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 <errno.h>
+#ifdef __APPLE__
+#include <malloc/malloc.h>
+#elif HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#include <krb5.h>
+#include <base64.h>
+#include <getarg.h>
+#include <roken.h>
+#include <vers.h>
+
+#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;
+}