1267 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1267 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;
 | |
|     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);
 | |
|     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(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;
 | |
| }
 | 
