git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@18493 ec53bebd-3082-4978-b11e-865c3cabbd6b
		
			
				
	
	
		
			1055 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1055 lines
		
	
	
		
			22 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 *handle;
 | 
						|
    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->handle;
 | 
						|
    c->handle = 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; }
 | 
						|
	    }
 | 
						|
	    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)
 | 
						|
{
 | 
						|
    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->handle, hContext, handle_context);
 | 
						|
    if (ctx == NULL)
 | 
						|
	hContext = 0;
 | 
						|
    creds = find_handle(c->handle, 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->handle, 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->handle, 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->handle, 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->handle, 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;
 | 
						|
    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 (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->handle, 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->handle, 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->handle, 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->handle, 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)
 | 
						|
{
 | 
						|
    errx(1, "AcquirePKInitCreds");
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
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 *
 | 
						|
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;
 | 
						|
    }
 | 
						|
    krb5_storage_free(c->sock);
 | 
						|
    if (c->logging)
 | 
						|
	krb5_storage_free(c->logging);
 | 
						|
 | 
						|
    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);
 | 
						|
    }
 | 
						|
 | 
						|
    krb5_free_context(context);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 |