1093 lines
23 KiB
C
1093 lines
23 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: gssmask.c 21229 2007-06-20 10:19:19Z lha $");
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
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;
|
|
int i = 0;
|
|
|
|
while (h) {
|
|
h = h->next;
|
|
i++;
|
|
}
|
|
|
|
if (i != 0)
|
|
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;
|
|
|
|
ret32(c, flags);
|
|
retdata(c, pfxdata);
|
|
|
|
/* get credentials */
|
|
|
|
krb5_data_free(&pfxdata);
|
|
|
|
put32(c, -1); /* hResource */
|
|
put32(c, GSMERR_NOT_SUPPORTED);
|
|
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)
|
|
};
|
|
|
|
#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));
|
|
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;
|
|
}
|