summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/appl/gssmask/gssmask.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /third_party/heimdal/appl/gssmask/gssmask.c
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/heimdal/appl/gssmask/gssmask.c')
-rw-r--r--third_party/heimdal/appl/gssmask/gssmask.c1268
1 files changed, 1268 insertions, 0 deletions
diff --git a/third_party/heimdal/appl/gssmask/gssmask.c b/third_party/heimdal/appl/gssmask/gssmask.c
new file mode 100644
index 0000000..44b59fe
--- /dev/null
+++ b/third_party/heimdal/appl/gssmask/gssmask.c
@@ -0,0 +1,1268 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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 in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``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 KTH OR ITS CONTRIBUTORS 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 "common.h"
+RCSID("$Id$");
+
+/*
+ *
+ */
+
+enum handle_type { handle_context, handle_cred };
+
+struct handle {
+ int32_t idx;
+ enum handle_type type;
+ void *ptr;
+ struct handle *next;
+};
+
+struct client {
+ krb5_storage *sock;
+ krb5_storage *logging;
+ char *moniker;
+ int32_t nHandle;
+ struct handle *handles;
+ struct sockaddr_storage sa;
+ socklen_t salen;
+ char servername[MAXHOSTNAMELEN];
+};
+
+FILE *logfile;
+static char *targetname;
+krb5_context context;
+
+/*
+ *
+ */
+
+static void
+logmessage(struct client *c, const char *file, unsigned int lineno,
+ int level, const char *fmt, ...)
+{
+ char *message;
+ va_list ap;
+ int32_t ackid;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&message, fmt, ap);
+ va_end(ap);
+ if (ret == -1)
+ errx(1, "out of memory");
+
+ if (logfile)
+ fprintf(logfile, "%s:%u: %d %s\n", file, lineno, level, message);
+
+ if (c->logging) {
+ if (krb5_store_int32(c->logging, eLogInfo) != 0)
+ errx(1, "krb5_store_int32: log level");
+ if (krb5_store_string(c->logging, file) != 0)
+ errx(1, "krb5_store_string: filename");
+ if (krb5_store_int32(c->logging, lineno) != 0)
+ errx(1, "krb5_store_string: filename");
+ if (krb5_store_string(c->logging, message) != 0)
+ errx(1, "krb5_store_string: message");
+ if (krb5_ret_int32(c->logging, &ackid) != 0)
+ errx(1, "krb5_ret_int32: ackid");
+ }
+ free(message);
+}
+
+/*
+ *
+ */
+
+static int32_t
+add_handle(struct client *c, enum handle_type type, void *data)
+{
+ struct handle *h;
+
+ h = ecalloc(1, sizeof(*h));
+
+ h->idx = ++c->nHandle;
+ h->type = type;
+ h->ptr = data;
+ h->next = c->handles;
+ c->handles = h;
+
+ return h->idx;
+}
+
+static void
+del_handle(struct handle **h, int32_t idx)
+{
+ OM_uint32 min_stat;
+
+ if (idx == 0)
+ return;
+
+ while (*h) {
+ if ((*h)->idx == idx) {
+ struct handle *p = *h;
+ *h = (*h)->next;
+ switch(p->type) {
+ case handle_context: {
+ gss_ctx_id_t c = p->ptr;
+ gss_delete_sec_context(&min_stat, &c, NULL);
+ break; }
+ case handle_cred: {
+ gss_cred_id_t c = p->ptr;
+ gss_release_cred(&min_stat, &c);
+ break; }
+ }
+ free(p);
+ return;
+ }
+ h = &((*h)->next);
+ }
+ errx(1, "tried to delete an unexisting handle");
+}
+
+static void *
+find_handle(struct handle *h, int32_t idx, enum handle_type type)
+{
+ if (idx == 0)
+ return NULL;
+
+ while (h) {
+ if (h->idx == idx) {
+ if (type == h->type)
+ return h->ptr;
+ errx(1, "monger switched type on handle!");
+ }
+ h = h->next;
+ }
+ return NULL;
+}
+
+
+static int32_t
+convert_gss_to_gsm(OM_uint32 maj_stat)
+{
+ switch(maj_stat) {
+ case 0:
+ return GSMERR_OK;
+ case GSS_S_CONTINUE_NEEDED:
+ return GSMERR_CONTINUE_NEEDED;
+ case GSS_S_DEFECTIVE_TOKEN:
+ return GSMERR_INVALID_TOKEN;
+ case GSS_S_BAD_MIC:
+ return GSMERR_AP_MODIFIED;
+ default:
+ return GSMERR_ERROR;
+ }
+}
+
+static int32_t
+convert_krb5_to_gsm(krb5_error_code ret)
+{
+ switch(ret) {
+ case 0:
+ return GSMERR_OK;
+ default:
+ return GSMERR_ERROR;
+ }
+}
+
+/*
+ *
+ */
+
+static int32_t
+acquire_cred(struct client *c,
+ krb5_principal principal,
+ krb5_get_init_creds_opt *opt,
+ int32_t *handle)
+{
+ krb5_error_code ret;
+ krb5_creds cred;
+ krb5_ccache id;
+ gss_cred_id_t gcred;
+ OM_uint32 maj_stat, min_stat;
+
+ *handle = 0;
+
+ krb5_get_init_creds_opt_set_forwardable (opt, 1);
+ krb5_get_init_creds_opt_set_renew_life (opt, 3600 * 24 * 30);
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_get_init_creds_password (context,
+ &cred,
+ principal,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ opt);
+ if (ret) {
+ logmessage(c, __FILE__, __LINE__, 0,
+ "krb5_get_init_creds failed: %d", ret);
+ return convert_krb5_to_gsm(ret);
+ }
+
+ ret = krb5_cc_new_unique(context, "MEMORY", NULL, &id);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_initialize (context, id, cred.client);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_store_cred (context, id, &cred);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_cc_store_cred");
+
+ krb5_free_cred_contents (context, &cred);
+
+ maj_stat = gss_krb5_import_cred(&min_stat,
+ id,
+ NULL,
+ NULL,
+ &gcred);
+ krb5_cc_close(context, id);
+ if (maj_stat) {
+ logmessage(c, __FILE__, __LINE__, 0,
+ "krb5 import creds failed with: %d", maj_stat);
+ return convert_gss_to_gsm(maj_stat);
+ }
+
+ *handle = add_handle(c, handle_cred, gcred);
+
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+#define HandleOP(h) \
+handle##h(enum gssMaggotOp op, struct client *c)
+
+/*
+ *
+ */
+
+static int
+HandleOP(GetVersionInfo)
+{
+ put32(c, GSSMAGGOTPROTOCOL);
+ errx(1, "GetVersionInfo");
+}
+
+static int
+HandleOP(GoodBye)
+{
+ struct handle *h = c->handles;
+ unsigned int i = 0;
+
+ while (h) {
+ h = h->next;
+ i++;
+ }
+
+ if (i)
+ logmessage(c, __FILE__, __LINE__, 0,
+ "Did not toast all resources: %d", i);
+ return 1;
+}
+
+static int
+HandleOP(InitContext)
+{
+ OM_uint32 maj_stat, min_stat, ret_flags;
+ int32_t hContext, hCred, flags;
+ krb5_data target_name, in_token;
+ int32_t new_context_id = 0, gsm_error = 0;
+ krb5_data out_token = { 0 , NULL };
+
+ gss_ctx_id_t ctx;
+ gss_cred_id_t creds;
+ gss_name_t gss_target_name;
+ gss_buffer_desc input_token;
+ gss_buffer_desc output_token = {0, 0};
+ gss_OID oid = GSS_C_NO_OID;
+ gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
+
+ ret32(c, hContext);
+ ret32(c, hCred);
+ ret32(c, flags);
+ retdata(c, target_name);
+ retdata(c, in_token);
+
+ logmessage(c, __FILE__, __LINE__, 0,
+ "targetname: <%.*s>", (int)target_name.length,
+ (char *)target_name.data);
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ hContext = 0;
+ creds = find_handle(c->handles, hCred, handle_cred);
+ if (creds == NULL)
+ abort();
+
+ input_token.length = target_name.length;
+ input_token.value = target_name.data;
+
+ maj_stat = gss_import_name(&min_stat,
+ &input_token,
+ GSS_KRB5_NT_PRINCIPAL_NAME,
+ &gss_target_name);
+ if (GSS_ERROR(maj_stat)) {
+ logmessage(c, __FILE__, __LINE__, 0,
+ "import name creds failed with: %d", maj_stat);
+ gsm_error = convert_gss_to_gsm(maj_stat);
+ goto out;
+ }
+
+ /* oid from flags */
+
+ if (in_token.length) {
+ input_token.length = in_token.length;
+ input_token.value = in_token.data;
+ input_token_ptr = &input_token;
+ if (ctx == NULL)
+ krb5_errx(context, 1, "initcreds, context NULL, but not first req");
+ } else {
+ input_token.length = 0;
+ input_token.value = NULL;
+ if (ctx)
+ krb5_errx(context, 1, "initcreds, context not NULL, but first req");
+ }
+
+ if ((flags & GSS_C_DELEG_FLAG) != 0)
+ logmessage(c, __FILE__, __LINE__, 0, "init_sec_context delegating");
+ if ((flags & GSS_C_DCE_STYLE) != 0)
+ logmessage(c, __FILE__, __LINE__, 0, "init_sec_context dce-style");
+
+ maj_stat = gss_init_sec_context(&min_stat,
+ creds,
+ &ctx,
+ gss_target_name,
+ oid,
+ flags & 0x7f,
+ 0,
+ NULL,
+ input_token_ptr,
+ NULL,
+ &output_token,
+ &ret_flags,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ if (hContext != 0)
+ del_handle(&c->handles, hContext);
+ new_context_id = 0;
+ logmessage(c, __FILE__, __LINE__, 0,
+ "gss_init_sec_context returns code: %d/%d",
+ maj_stat, min_stat);
+ } else {
+ if (input_token.length == 0)
+ new_context_id = add_handle(c, handle_context, ctx);
+ else
+ new_context_id = hContext;
+ }
+
+ gsm_error = convert_gss_to_gsm(maj_stat);
+
+ if (output_token.length) {
+ out_token.data = output_token.value;
+ out_token.length = output_token.length;
+ }
+
+out:
+ logmessage(c, __FILE__, __LINE__, 0,
+ "InitContext return code: %d", gsm_error);
+
+ put32(c, new_context_id);
+ put32(c, gsm_error);
+ putdata(c, out_token);
+
+ gss_release_name(&min_stat, &gss_target_name);
+ if (output_token.length)
+ gss_release_buffer(&min_stat, &output_token);
+ krb5_data_free(&in_token);
+ krb5_data_free(&target_name);
+
+ return 0;
+}
+
+static int
+HandleOP(AcceptContext)
+{
+ OM_uint32 maj_stat, min_stat, ret_flags;
+ int32_t hContext, deleg_hcred, flags;
+ krb5_data in_token;
+ int32_t new_context_id = 0, gsm_error = 0;
+ krb5_data out_token = { 0 , NULL };
+
+ gss_ctx_id_t ctx;
+ gss_cred_id_t deleg_cred = GSS_C_NO_CREDENTIAL;
+ gss_buffer_desc input_token, output_token;
+
+ ret32(c, hContext);
+ ret32(c, flags);
+ retdata(c, in_token);
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ hContext = 0;
+
+ if (in_token.length) {
+ input_token.length = in_token.length;
+ input_token.value = in_token.data;
+ } else {
+ input_token.length = 0;
+ input_token.value = NULL;
+ }
+
+ maj_stat = gss_accept_sec_context(&min_stat,
+ &ctx,
+ GSS_C_NO_CREDENTIAL,
+ &input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ NULL,
+ NULL,
+ &output_token,
+ &ret_flags,
+ NULL,
+ &deleg_cred);
+ if (GSS_ERROR(maj_stat)) {
+ if (hContext != 0)
+ del_handle(&c->handles, hContext);
+ logmessage(c, __FILE__, __LINE__, 0,
+ "gss_accept_sec_context returns code: %d/%d",
+ maj_stat, min_stat);
+ new_context_id = 0;
+ } else {
+ if (hContext == 0)
+ new_context_id = add_handle(c, handle_context, ctx);
+ else
+ new_context_id = hContext;
+ }
+ if (output_token.length) {
+ out_token.data = output_token.value;
+ out_token.length = output_token.length;
+ }
+ if ((ret_flags & GSS_C_DCE_STYLE) != 0)
+ logmessage(c, __FILE__, __LINE__, 0, "accept_sec_context dce-style");
+ if ((ret_flags & GSS_C_DELEG_FLAG) != 0) {
+ deleg_hcred = add_handle(c, handle_cred, deleg_cred);
+ logmessage(c, __FILE__, __LINE__, 0,
+ "accept_context delegated handle: %d", deleg_hcred);
+ } else {
+ gss_release_cred(&min_stat, &deleg_cred);
+ deleg_hcred = 0;
+ }
+
+
+ gsm_error = convert_gss_to_gsm(maj_stat);
+
+ put32(c, new_context_id);
+ put32(c, gsm_error);
+ putdata(c, out_token);
+ put32(c, deleg_hcred);
+
+ if (output_token.length)
+ gss_release_buffer(&min_stat, &output_token);
+ krb5_data_free(&in_token);
+
+ return 0;
+}
+
+static int
+HandleOP(ToastResource)
+{
+ int32_t handle;
+
+ ret32(c, handle);
+ logmessage(c, __FILE__, __LINE__, 0, "toasting %d", handle);
+ del_handle(&c->handles, handle);
+ put32(c, GSMERR_OK);
+
+ return 0;
+}
+
+static int
+HandleOP(AcquireCreds)
+{
+ char *name, *password;
+ int32_t gsm_error, flags, handle = 0;
+ krb5_principal principal = NULL;
+ krb5_get_init_creds_opt *opt = NULL;
+ krb5_error_code ret;
+
+ retstring(c, name);
+ retstring(c, password);
+ ret32(c, flags);
+
+ logmessage(c, __FILE__, __LINE__, 0,
+ "username: %s password: %s", name, password);
+
+ ret = krb5_parse_name(context, name, &principal);
+ if (ret) {
+ gsm_error = convert_krb5_to_gsm(ret);
+ goto out;
+ }
+
+ ret = krb5_get_init_creds_opt_alloc (context, &opt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
+
+ krb5_get_init_creds_opt_set_pa_password(context, opt, password, NULL);
+
+ gsm_error = acquire_cred(c, principal, opt, &handle);
+
+out:
+ logmessage(c, __FILE__, __LINE__, 0,
+ "AcquireCreds handle: %d return code: %d", handle, gsm_error);
+
+ if (opt)
+ krb5_get_init_creds_opt_free (context, opt);
+ if (principal)
+ krb5_free_principal(context, principal);
+ free(name);
+ free(password);
+
+ put32(c, gsm_error);
+ put32(c, handle);
+
+ return 0;
+}
+
+static int
+HandleOP(Sign)
+{
+ OM_uint32 maj_stat, min_stat;
+ int32_t hContext, flags, seqno;
+ krb5_data token;
+ gss_ctx_id_t ctx;
+ gss_buffer_desc input_token, output_token;
+
+ ret32(c, hContext);
+ ret32(c, flags);
+ ret32(c, seqno);
+ retdata(c, token);
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ errx(1, "sign: reference to unknown context");
+
+ input_token.length = token.length;
+ input_token.value = token.data;
+
+ maj_stat = gss_get_mic(&min_stat, ctx, 0, &input_token,
+ &output_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_get_mic failed");
+
+ krb5_data_free(&token);
+
+ token.data = output_token.value;
+ token.length = output_token.length;
+
+ put32(c, 0); /* XXX fix gsm_error */
+ putdata(c, token);
+
+ gss_release_buffer(&min_stat, &output_token);
+
+ return 0;
+}
+
+static int
+HandleOP(Verify)
+{
+ OM_uint32 maj_stat, min_stat;
+ int32_t hContext, flags, seqno;
+ krb5_data msg, mic;
+ gss_ctx_id_t ctx;
+ gss_buffer_desc msg_token, mic_token;
+ gss_qop_t qop;
+
+ ret32(c, hContext);
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ errx(1, "verify: reference to unknown context");
+
+ ret32(c, flags);
+ ret32(c, seqno);
+ retdata(c, msg);
+
+ msg_token.length = msg.length;
+ msg_token.value = msg.data;
+
+ retdata(c, mic);
+
+ mic_token.length = mic.length;
+ mic_token.value = mic.data;
+
+ maj_stat = gss_verify_mic(&min_stat, ctx, &msg_token,
+ &mic_token, &qop);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_verify_mic failed");
+
+ krb5_data_free(&mic);
+ krb5_data_free(&msg);
+
+ put32(c, 0); /* XXX fix gsm_error */
+
+ return 0;
+}
+
+static int
+HandleOP(GetVersionAndCapabilities)
+{
+ int32_t cap = HAS_MONIKER;
+ char name[256] = "unknown", *str;
+ int ret;
+
+ if (targetname)
+ cap |= ISSERVER; /* is server */
+
+#ifdef HAVE_UNAME
+ {
+ struct utsname ut;
+ if (uname(&ut) == 0) {
+ snprintf(name, sizeof(name), "%s-%s-%s",
+ ut.sysname, ut.version, ut.machine);
+ }
+ }
+#endif
+
+ ret = asprintf(&str, "gssmask %s %s", PACKAGE_STRING, name);
+ if (ret == -1)
+ errx(1, "out of memory");
+
+ put32(c, GSSMAGGOTPROTOCOL);
+ put32(c, cap);
+ putstring(c, str);
+ free(str);
+
+ return 0;
+}
+
+static int
+HandleOP(GetTargetName)
+{
+ if (targetname)
+ putstring(c, targetname);
+ else
+ putstring(c, "");
+ return 0;
+}
+
+static int
+HandleOP(SetLoggingSocket)
+{
+ int32_t portnum;
+ krb5_socket_t sock;
+ int ret;
+
+ ret32(c, portnum);
+
+ logmessage(c, __FILE__, __LINE__, 0,
+ "logging port on peer is: %d", (int)portnum);
+
+ socket_set_port((struct sockaddr *)(&c->sa), htons(portnum));
+
+ sock = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0);
+ if (sock == rk_INVALID_SOCKET)
+ return 0;
+
+ ret = connect(sock, (struct sockaddr *)&c->sa, c->salen);
+ if (ret < 0) {
+ logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s",
+ strerror(errno));
+ rk_closesocket(sock);
+ return 0;
+ }
+
+ if (c->logging)
+ krb5_storage_free(c->logging);
+ c->logging = krb5_storage_from_socket(sock);
+ rk_closesocket(sock);
+
+ krb5_store_int32(c->logging, eLogSetMoniker);
+ store_string(c->logging, c->moniker);
+
+ logmessage(c, __FILE__, __LINE__, 0, "logging turned on");
+
+ return 0;
+}
+
+
+static int
+HandleOP(ChangePassword)
+{
+ errx(1, "ChangePassword");
+}
+
+static int
+HandleOP(SetPasswordSelf)
+{
+ errx(1, "SetPasswordSelf");
+}
+
+static int
+HandleOP(Wrap)
+{
+ OM_uint32 maj_stat, min_stat;
+ int32_t hContext, flags, seqno;
+ krb5_data token;
+ gss_ctx_id_t ctx;
+ gss_buffer_desc input_token, output_token;
+ int conf_state;
+
+ ret32(c, hContext);
+ ret32(c, flags);
+ ret32(c, seqno);
+ retdata(c, token);
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ errx(1, "wrap: reference to unknown context");
+
+ input_token.length = token.length;
+ input_token.value = token.data;
+
+ maj_stat = gss_wrap(&min_stat, ctx, flags, 0, &input_token,
+ &conf_state, &output_token);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_wrap failed");
+
+ krb5_data_free(&token);
+
+ token.data = output_token.value;
+ token.length = output_token.length;
+
+ put32(c, 0); /* XXX fix gsm_error */
+ putdata(c, token);
+
+ gss_release_buffer(&min_stat, &output_token);
+
+ return 0;
+}
+
+
+static int
+HandleOP(Unwrap)
+{
+ OM_uint32 maj_stat, min_stat;
+ int32_t hContext, flags, seqno;
+ krb5_data token;
+ gss_ctx_id_t ctx;
+ gss_buffer_desc input_token, output_token;
+ int conf_state;
+ gss_qop_t qop_state;
+
+ ret32(c, hContext);
+ ret32(c, flags);
+ ret32(c, seqno);
+ retdata(c, token);
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ errx(1, "unwrap: reference to unknown context");
+
+ input_token.length = token.length;
+ input_token.value = token.data;
+
+ maj_stat = gss_unwrap(&min_stat, ctx, &input_token,
+ &output_token, &conf_state, &qop_state);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
+
+ krb5_data_free(&token);
+ if (maj_stat == GSS_S_COMPLETE) {
+ token.data = output_token.value;
+ token.length = output_token.length;
+ } else {
+ token.data = NULL;
+ token.length = 0;
+ }
+ put32(c, 0); /* XXX fix gsm_error */
+ putdata(c, token);
+
+ if (maj_stat == GSS_S_COMPLETE)
+ gss_release_buffer(&min_stat, &output_token);
+
+ return 0;
+}
+
+static int
+HandleOP(Encrypt)
+{
+ return handleWrap(op, c);
+}
+
+static int
+HandleOP(Decrypt)
+{
+ return handleUnwrap(op, c);
+}
+
+static int
+HandleOP(ConnectLoggingService2)
+{
+ errx(1, "ConnectLoggingService2");
+}
+
+static int
+HandleOP(GetMoniker)
+{
+ putstring(c, c->moniker);
+ return 0;
+}
+
+static int
+HandleOP(CallExtension)
+{
+ errx(1, "CallExtension");
+}
+
+static int
+HandleOP(AcquirePKInitCreds)
+{
+ int32_t flags;
+ krb5_data pfxdata;
+ char fn[] = "FILE:/tmp/pkcs12-creds-XXXXXXX";
+ int fd;
+
+ ret32(c, flags);
+ retdata(c, pfxdata);
+
+ fd = mkstemp(fn + 5);
+ if (fd < 0)
+ errx(1, "mkstemp");
+
+ net_write(fd, pfxdata.data, pfxdata.length);
+ krb5_data_free(&pfxdata);
+ close(fd);
+
+ put32(c, -1); /* hResource */
+ put32(c, GSMERR_NOT_SUPPORTED);
+ return 0;
+}
+
+static int
+HandleOP(WrapExt)
+{
+ OM_uint32 maj_stat, min_stat;
+ int32_t hContext, flags, bflags;
+ krb5_data token, header, trailer;
+ gss_ctx_id_t ctx;
+ unsigned char *p;
+ int conf_state, iov_len;
+ gss_iov_buffer_desc iov[6];
+
+ ret32(c, hContext);
+ ret32(c, flags);
+ ret32(c, bflags);
+ retdata(c, header);
+ retdata(c, token);
+ retdata(c, trailer);
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ errx(1, "wrap: reference to unknown context");
+
+ memset(&iov, 0, sizeof(iov));
+
+ iov_len = sizeof(iov)/sizeof(iov[0]);
+
+ if (bflags & WRAP_EXP_ONLY_HEADER)
+ iov_len -= 2; /* skip trailer and padding, aka dce-style */
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
+ if (header.length != 0) {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[1].buffer.length = header.length;
+ iov[1].buffer.value = header.data;
+ } else {
+ iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ }
+ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[2].buffer.length = token.length;
+ iov[2].buffer.value = token.data;
+ if (trailer.length != 0) {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[3].buffer.length = trailer.length;
+ iov[3].buffer.value = trailer.data;
+ } else {
+ iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ }
+ iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
+ iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
+
+ maj_stat = gss_wrap_iov_length(&min_stat, ctx, flags, 0, &conf_state,
+ iov, iov_len);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_wrap_iov_length failed");
+
+ maj_stat = gss_wrap_iov(&min_stat, ctx, flags, 0, &conf_state,
+ iov, iov_len);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_wrap_iov failed");
+
+ krb5_data_free(&token);
+
+ token.length = iov[0].buffer.length + iov[2].buffer.length + iov[4].buffer.length + iov[5].buffer.length;
+ token.data = malloc(token.length);
+
+ p = token.data;
+ memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
+ p += iov[0].buffer.length;
+ memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
+ p += iov[2].buffer.length;
+ memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
+ p += iov[4].buffer.length;
+ memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
+#if 0 /* Would be needed to keep going, but presently unused */
+ p += iov[5].buffer.length;
+#endif
+
+ gss_release_iov_buffer(NULL, iov, iov_len);
+
+ put32(c, 0); /* XXX fix gsm_error */
+ putdata(c, token);
+
+ free(token.data);
+
+ return 0;
+}
+
+
+static int
+HandleOP(UnwrapExt)
+{
+ OM_uint32 maj_stat, min_stat;
+ int32_t hContext, flags, bflags;
+ krb5_data token, header, trailer;
+ gss_ctx_id_t ctx;
+ gss_iov_buffer_desc iov[3];
+ int conf_state, iov_len;
+ gss_qop_t qop_state;
+
+ ret32(c, hContext);
+ ret32(c, flags);
+ ret32(c, bflags);
+ retdata(c, header);
+ retdata(c, token);
+ retdata(c, trailer);
+
+ iov_len = sizeof(iov)/sizeof(iov[0]);
+
+ if (bflags & WRAP_EXP_ONLY_HEADER)
+ iov_len -= 1; /* skip trailer and padding, aka dce-style */
+
+ ctx = find_handle(c->handles, hContext, handle_context);
+ if (ctx == NULL)
+ errx(1, "unwrap: reference to unknown context");
+
+ if (header.length != 0) {
+ iov[0].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[0].buffer.length = header.length;
+ iov[0].buffer.value = header.data;
+ } else {
+ iov[0].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ }
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[1].buffer.length = token.length;
+ iov[1].buffer.value = token.data;
+
+ if (trailer.length != 0) {
+ iov[2].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov[2].buffer.length = trailer.length;
+ iov[2].buffer.value = trailer.data;
+ } else {
+ iov[2].type = GSS_IOV_BUFFER_TYPE_EMPTY;
+ }
+
+ maj_stat = gss_unwrap_iov(&min_stat, ctx, &conf_state, &qop_state,
+ iov, iov_len);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap failed: %d/%d", maj_stat, min_stat);
+
+ if (maj_stat == GSS_S_COMPLETE) {
+ token.data = iov[1].buffer.value;
+ token.length = iov[1].buffer.length;
+ } else {
+ token.data = NULL;
+ token.length = 0;
+ }
+ put32(c, 0); /* XXX fix gsm_error */
+ putdata(c, token);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct handler {
+ enum gssMaggotOp op;
+ const char *name;
+ int (*func)(enum gssMaggotOp, struct client *);
+};
+
+#define S(a) { e##a, #a, handle##a }
+
+struct handler handlers[] = {
+ S(GetVersionInfo),
+ S(GoodBye),
+ S(InitContext),
+ S(AcceptContext),
+ S(ToastResource),
+ S(AcquireCreds),
+ S(Encrypt),
+ S(Decrypt),
+ S(Sign),
+ S(Verify),
+ S(GetVersionAndCapabilities),
+ S(GetTargetName),
+ S(SetLoggingSocket),
+ S(ChangePassword),
+ S(SetPasswordSelf),
+ S(Wrap),
+ S(Unwrap),
+ S(ConnectLoggingService2),
+ S(GetMoniker),
+ S(CallExtension),
+ S(AcquirePKInitCreds),
+ S(WrapExt),
+ S(UnwrapExt),
+};
+
+#undef S
+
+/*
+ *
+ */
+
+static struct handler *
+find_op(int32_t op)
+{
+ int i;
+
+ for (i = 0; i < sizeof(handlers)/sizeof(handlers[0]); i++)
+ if (handlers[i].op == op)
+ return &handlers[i];
+ return NULL;
+}
+
+static struct client *
+create_client(krb5_socket_t sock, int port, const char *moniker)
+{
+ struct client *c;
+ int ret;
+
+ c = ecalloc(1, sizeof(*c));
+
+ if (moniker) {
+ c->moniker = estrdup(moniker);
+ } else {
+ char hostname[MAXHOSTNAMELEN];
+ gethostname(hostname, sizeof(hostname));
+ ret = asprintf(&c->moniker, "gssmask: %s:%d", hostname, port);
+ if (ret == -1)
+ c->moniker = NULL;
+ }
+
+ if (!c->moniker)
+ errx(1, "out of memory");
+
+ {
+ c->salen = sizeof(c->sa);
+ getpeername(sock, (struct sockaddr *)&c->sa, &c->salen);
+
+ getnameinfo((struct sockaddr *)&c->sa, c->salen,
+ c->servername, sizeof(c->servername),
+ NULL, 0, NI_NUMERICHOST);
+ }
+
+ c->sock = krb5_storage_from_socket(sock);
+ if (c->sock == NULL)
+ errx(1, "krb5_storage_from_socket");
+
+ rk_closesocket(sock);
+
+ return c;
+}
+
+static void
+free_client(struct client *c)
+{
+ while(c->handles)
+ del_handle(&c->handles, c->handles->idx);
+
+ free(c->moniker);
+ krb5_storage_free(c->sock);
+ if (c->logging)
+ krb5_storage_free(c->logging);
+ free(c);
+}
+
+
+static void *
+handleServer(void *ptr)
+{
+ struct handler *handler;
+ struct client *c;
+ int32_t op;
+
+ c = (struct client *)ptr;
+
+
+ while(1) {
+ ret32(c, op);
+
+ handler = find_op(op);
+ if (handler == NULL) {
+ logmessage(c, __FILE__, __LINE__, 0,
+ "op %d not supported", (int)op);
+ exit(1);
+ }
+
+ logmessage(c, __FILE__, __LINE__, 0,
+ "---> Got op %s from server %s",
+ handler->name, c->servername);
+
+ if ((handler->func)(handler->op, c))
+ break;
+ }
+
+ return NULL;
+}
+
+
+static char *port_str;
+static int version_flag;
+static int help_flag;
+static char *logfile_str;
+static char *moniker_str;
+
+static int port = 4711;
+
+struct getargs args[] = {
+ { "spn", 0, arg_string, &targetname, "This host's SPN",
+ "service/host@REALM" },
+ { "port", 'p', arg_string, &port_str, "Use this port",
+ "number-of-service" },
+ { "logfile", 0, arg_string, &logfile_str, "logfile",
+ "number-of-service" },
+ { "moniker", 0, arg_string, &moniker_str, "nickname",
+ "name" },
+ { "version", 0, arg_flag, &version_flag, "Print version",
+ NULL },
+ { "help", 0, arg_flag, &help_flag, NULL,
+ NULL }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage (args,
+ sizeof(args) / sizeof(args[0]),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+ krb5_error_code ret;
+
+ setprogname (argv[0]);
+
+ if (getarg (args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage (1);
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag) {
+ print_version (NULL);
+ return 0;
+ }
+
+ if (optidx != argc)
+ usage (1);
+
+ if (port_str) {
+ char *ptr;
+
+ port = strtol (port_str, &ptr, 10);
+ if (port == 0 && ptr == port_str)
+ errx (1, "Bad port `%s'", port_str);
+ }
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "Error initializing kerberos: %d", ret);
+
+ {
+ const char *lf = logfile_str;
+ if (lf == NULL)
+ lf = "/dev/tty";
+
+ logfile = fopen(lf, "w");
+ if (logfile == NULL)
+ err(1, "error opening %s", lf);
+ }
+
+ mini_inetd(htons(port), NULL);
+ fprintf(logfile, "connected\n");
+
+ {
+ struct client *c;
+
+ c = create_client(0, port, moniker_str);
+ /* close(0); */
+
+ handleServer(c);
+
+ free_client(c);
+ }
+
+ krb5_free_context(context);
+
+ return 0;
+}