freebsd-dev/crypto/heimdal/appl/gssmask/gssmask.c
Stanislav Sedov ae77177087 - Update FreeBSD Heimdal distribution to version 1.5.1. This also brings
several new kerberos related libraries and applications to FreeBSD:
  o kgetcred(1) allows one to manually get a ticket for a particular service.
  o kf(1) securily forwards ticket to another host through an authenticated
    and encrypted stream.
  o kcc(1) is an umbrella program around klist(1), kswitch(1), kgetcred(1)
    and other user kerberos operations. klist and kswitch are just symlinks
    to kcc(1) now.
  o kswitch(1) allows you to easily switch between kerberos credentials if
    you're running KCM.
  o hxtool(1) is a certificate management tool to use with PKINIT.
  o string2key(1) maps a password into key.
  o kdigest(8) is a userland tool to access the KDC's digest interface.
  o kimpersonate(8) creates a "fake" ticket for a service.

  We also now install manpages for some lirbaries that were not installed
  before, libheimntlm and libhx509.

- The new HEIMDAL version no longer supports Kerberos 4.  All users are
  recommended to switch to Kerberos 5.

- Weak ciphers are now disabled by default.  To enable DES support (used
  by telnet(8)), use "allow_weak_crypto" option in krb5.conf.

- libtelnet, pam_ksu and pam_krb5 are now compiled with error on warnings
  disabled due to the function they use (krb5_get_err_text(3)) being
  deprecated.  I plan to work on this next.

- Heimdal's KDC now require sqlite to operate.  We use the bundled version
  and install it as libheimsqlite.  If some other FreeBSD components will
  require it in the future we can rename it to libbsdsqlite and use for these
  components as well.

- This is not a latest Heimdal version, the new one was released while I was
  working on the update.  I will update it to 1.5.2 soon, as it fixes some
  important bugs and security issues.
2012-03-22 08:48:42 +00:00

1256 lines
27 KiB
C

/*
* 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;
va_start(ap, fmt);
vasprintf(&message, fmt, ap);
va_end(ap);
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, output_token;
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;
gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
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;
input_token_ptr = &input_token;
} 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;
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
asprintf(&str, "gssmask %s %s", PACKAGE_STRING, name);
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;
int fd, 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));
fd = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0);
if (fd < 0)
return 0;
ret = connect(fd, (struct sockaddr *)&c->sa, c->salen);
if (ret < 0) {
logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s",
strerror(errno));
close(fd);
return 0;
}
if (c->logging)
krb5_storage_free(c->logging);
c->logging = krb5_storage_from_fd(fd);
close(fd);
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";
krb5_principal principal = NULL;
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);
if (principal)
krb5_free_principal(context, principal);
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);
p += iov[5].buffer.length;
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(int fd, int port, const char *moniker)
{
struct client *c;
c = ecalloc(1, sizeof(*c));
if (moniker) {
c->moniker = estrdup(moniker);
} else {
char hostname[MAXHOSTNAMELEN];
gethostname(hostname, sizeof(hostname));
asprintf(&c->moniker, "gssmask: %s:%d", hostname, port);
}
{
c->salen = sizeof(c->sa);
getpeername(fd, (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_fd(fd);
if (c->sock == NULL)
errx(1, "krb5_storage_from_fd");
close(fd);
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;
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);
}
krb5_init_context(&context);
{
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;
}