diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /third_party/heimdal/appl/gssmask/gssmask.c | |
parent | Initial commit. (diff) | |
download | samba-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.c | 1268 |
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; +} |