version 4 support
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@6101 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
785
kadmin/version4.c
Normal file
785
kadmin/version4.c
Normal file
@@ -0,0 +1,785 @@
|
||||
/*
|
||||
* Copyright (c) 1999 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 "kadmin_locl.h"
|
||||
#include <krb5-private.h>
|
||||
|
||||
#define Principal krb4_Principal
|
||||
#define kadm_get krb4_kadm_get
|
||||
#undef ALLOC
|
||||
#include <krb.h>
|
||||
#include <kadm.h>
|
||||
#include <krb_err.h>
|
||||
#include <kadm_err.h>
|
||||
|
||||
RCSID("$Id$");
|
||||
|
||||
#define KADM_NO_OPCODE -1
|
||||
#define KADM_NO_ENCRYPT -2
|
||||
|
||||
static void
|
||||
make_error_packet(int code, krb5_data *reply)
|
||||
{
|
||||
krb5_data_alloc(reply, KADM_VERSIZE + 4);
|
||||
memcpy(reply->data, KADM_ULOSE, KADM_VERSIZE);
|
||||
_krb5_put_int((char*)reply->data + KADM_VERSIZE, code, 4);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ret_fields(krb5_storage *sp, char *fields)
|
||||
{
|
||||
return sp->fetch(sp, fields, FLDSZ);
|
||||
}
|
||||
|
||||
static int
|
||||
store_fields(krb5_storage *sp, char *fields)
|
||||
{
|
||||
return sp->store(sp, fields, FLDSZ);
|
||||
}
|
||||
|
||||
static void
|
||||
ret_vals(krb5_storage *sp, Kadm_vals *vals)
|
||||
{
|
||||
int field;
|
||||
char *tmp_string;
|
||||
|
||||
memset(vals, 0, sizeof(*vals));
|
||||
|
||||
ret_fields(sp, vals->fields);
|
||||
|
||||
for(field = 31; field >= 0; field--) {
|
||||
if(IS_FIELD(field, vals->fields)) {
|
||||
switch(field) {
|
||||
case KADM_NAME:
|
||||
krb5_ret_stringz(sp, &tmp_string);
|
||||
strcpy_truncate(vals->name, tmp_string, sizeof(vals->name));
|
||||
free(tmp_string);
|
||||
break;
|
||||
case KADM_INST:
|
||||
krb5_ret_stringz(sp, &tmp_string);
|
||||
strcpy_truncate(vals->instance, tmp_string,
|
||||
sizeof(vals->instance));
|
||||
free(tmp_string);
|
||||
break;
|
||||
case KADM_EXPDATE:
|
||||
krb5_ret_int32(sp, &vals->exp_date);
|
||||
break;
|
||||
case KADM_ATTR:
|
||||
krb5_ret_int16(sp, &vals->attributes);
|
||||
break;
|
||||
case KADM_MAXLIFE:
|
||||
krb5_ret_int8(sp, &vals->max_life);
|
||||
break;
|
||||
case KADM_DESKEY:
|
||||
krb5_ret_int32(sp, &vals->key_high);
|
||||
krb5_ret_int32(sp, &vals->key_low);
|
||||
break;
|
||||
#ifdef EXTENDED_KADM
|
||||
case KADM_MODDATE:
|
||||
krb5_ret_int32(sp, &vals->mod_date);
|
||||
break;
|
||||
case KADM_MODNAME:
|
||||
krb5_ret_stringz(sp, &tmp_string);
|
||||
strcpy_truncate(vals->mod_name, tmp_string,
|
||||
sizeof(vals->mod_name));
|
||||
free(tmp_string);
|
||||
break;
|
||||
case KADM_MODINST:
|
||||
krb5_ret_stringz(sp, &tmp_string);
|
||||
strcpy_truncate(vals->mod_instance, tmp_string,
|
||||
sizeof(vals->mod_instance));
|
||||
free(tmp_string);
|
||||
break;
|
||||
case KADM_KVNO:
|
||||
krb5_ret_int8(sp, &vals->key_version);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
store_vals(krb5_storage *sp, Kadm_vals *vals)
|
||||
{
|
||||
int field;
|
||||
|
||||
store_fields(sp, vals->fields);
|
||||
|
||||
for(field = 31; field >= 0; field--) {
|
||||
if(IS_FIELD(field, vals->fields)) {
|
||||
switch(field) {
|
||||
case KADM_NAME:
|
||||
krb5_store_stringz(sp, vals->name);
|
||||
break;
|
||||
case KADM_INST:
|
||||
krb5_store_stringz(sp, vals->instance);
|
||||
break;
|
||||
case KADM_EXPDATE:
|
||||
krb5_store_int32(sp, vals->exp_date);
|
||||
break;
|
||||
case KADM_ATTR:
|
||||
krb5_store_int16(sp, vals->attributes);
|
||||
break;
|
||||
case KADM_MAXLIFE:
|
||||
krb5_store_int8(sp, vals->max_life);
|
||||
break;
|
||||
case KADM_DESKEY:
|
||||
krb5_store_int32(sp, vals->key_high);
|
||||
krb5_store_int32(sp, vals->key_low);
|
||||
break;
|
||||
#ifdef EXTENDED_KADM
|
||||
case KADM_MODDATE:
|
||||
krb5_store_int32(sp, vals->mod_date);
|
||||
break;
|
||||
case KADM_MODNAME:
|
||||
krb5_store_stringz(sp, vals->mod_name);
|
||||
break;
|
||||
case KADM_MODINST:
|
||||
krb5_store_stringz(sp, vals->mod_instance);
|
||||
break;
|
||||
case KADM_KVNO:
|
||||
krb5_store_int8(sp, vals->key_version);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
flags_4_to_5(char *flags)
|
||||
{
|
||||
int i;
|
||||
int32_t mask = 0;
|
||||
for(i = 31; i >= 0; i--) {
|
||||
if(IS_FIELD(i, flags))
|
||||
switch(i) {
|
||||
case KADM_NAME:
|
||||
case KADM_INST:
|
||||
mask |= KADM5_PRINCIPAL;
|
||||
case KADM_EXPDATE:
|
||||
mask |= KADM5_PW_EXPIRATION;
|
||||
case KADM_MAXLIFE:
|
||||
mask |= KADM5_MAX_LIFE;
|
||||
#ifdef EXTENDED_KADM
|
||||
case KADM_KVNO:
|
||||
mask |= KADM5_KEY_DATA;
|
||||
case KADM_MODDATE:
|
||||
mask |= KADM5_MOD_TIME;
|
||||
case KADM_MODNAME:
|
||||
case KADM_MODINST:
|
||||
mask |= KADM5_MOD_NAME;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void
|
||||
ent_to_values(krb5_context context,
|
||||
kadm5_principal_ent_t ent,
|
||||
int32_t mask,
|
||||
Kadm_vals *vals)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char realm[REALM_SZ];
|
||||
memset(vals, 0, sizeof(*vals));
|
||||
if(mask & KADM5_PRINCIPAL) {
|
||||
ret = krb5_524_conv_principal(context, ent->principal,
|
||||
vals->name, vals->instance, realm);
|
||||
SET_FIELD(KADM_NAME, vals->fields);
|
||||
SET_FIELD(KADM_INST, vals->fields);
|
||||
}
|
||||
if(mask & KADM5_PW_EXPIRATION) {
|
||||
time_t exp = 0;
|
||||
if(ent->princ_expire_time != 0)
|
||||
exp = ent->princ_expire_time;
|
||||
if(ent->pw_expiration != 0 && (exp == 0 || exp > ent->pw_expiration))
|
||||
exp = ent->pw_expiration;
|
||||
if(exp) {
|
||||
vals->exp_date = exp;
|
||||
SET_FIELD(KADM_EXPDATE, vals->fields);
|
||||
}
|
||||
}
|
||||
if(mask & KADM5_MAX_LIFE) {
|
||||
if(ent->max_life == 0)
|
||||
vals->max_life = 255;
|
||||
else
|
||||
vals->max_life = krb_time_to_life(0, ent->max_life);
|
||||
SET_FIELD(KADM_MAXLIFE, vals->fields);
|
||||
}
|
||||
if(mask & KADM5_KEY_DATA) {
|
||||
if(ent->n_key_data > 0) {
|
||||
#ifdef EXTENDED_KADM
|
||||
vals->key_version = ent->key_data[0].key_data_kvno;
|
||||
SET_FIELD(KADM_KVNO, vals->fields);
|
||||
#endif
|
||||
}
|
||||
/* XXX the key itself? */
|
||||
}
|
||||
#ifdef EXTENDED_KADM
|
||||
if(mask & KADM5_MOD_TIME) {
|
||||
vals->mod_date = ent->mod_date;
|
||||
SET_FIELD(KADM_MODDATE, vals->fields);
|
||||
}
|
||||
if(mask & KADM5_MOD_NAME) {
|
||||
krb5_524_conv_principal(context, ent->mod_name,
|
||||
vals->mod_name, vals->mod_instance, realm);
|
||||
SET_FIELD(KADM_MODNAME, vals->fields);
|
||||
SET_FIELD(KADM_MODINST, vals->fields);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
values_to_ent(krb5_context context,
|
||||
Kadm_vals *vals,
|
||||
kadm5_principal_ent_t ent,
|
||||
int32_t *mask)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
*mask = 0;
|
||||
memset(ent, 0, sizeof(*ent));
|
||||
|
||||
if(IS_FIELD(KADM_NAME, vals->fields)) {
|
||||
char *inst = NULL;
|
||||
if(IS_FIELD(KADM_INST, vals->fields))
|
||||
inst = vals->instance;
|
||||
ret = krb5_425_conv_principal(context,
|
||||
vals->name,
|
||||
inst,
|
||||
NULL,
|
||||
&ent->principal);
|
||||
if(ret)
|
||||
return ret;
|
||||
*mask |= KADM5_PRINCIPAL;
|
||||
}
|
||||
if(IS_FIELD(KADM_EXPDATE, vals->fields)) {
|
||||
ent->pw_expiration = vals->exp_date;
|
||||
*mask |= KADM5_PW_EXPIRATION;
|
||||
}
|
||||
if(IS_FIELD(KADM_MAXLIFE, vals->fields)) {
|
||||
ent->max_life = krb_life_to_time(0, vals->max_life);
|
||||
*mask |= KADM5_MAX_LIFE;
|
||||
}
|
||||
|
||||
if(IS_FIELD(KADM_DESKEY, vals->fields)) {
|
||||
int i;
|
||||
ent->key_data = calloc(3, sizeof(*ent->key_data));
|
||||
if(ent->key_data == NULL)
|
||||
return ENOMEM;
|
||||
for(i = 0; i < 3; i++) {
|
||||
ent->key_data[i].key_data_ver = 2;
|
||||
#ifdef EXTENDED_KADM
|
||||
if(IS_FIELD(KADM_KVNO, vals->fields))
|
||||
ent->key_data[i].key_data_kvno = vals->key_version;
|
||||
#endif
|
||||
ent->key_data[i].key_data_type[0] = ETYPE_DES_CBC_MD5;
|
||||
ent->key_data[i].key_data_length[0] = 8;
|
||||
if((ent->key_data[i].key_data_contents[0] = malloc(8)) == NULL)
|
||||
return ENOMEM;
|
||||
memcpy(ent->key_data[i].key_data_contents[0],
|
||||
&vals->key_high,
|
||||
4);
|
||||
memcpy((char*)ent->key_data[i].key_data_contents[0] + 4,
|
||||
&vals->key_low,
|
||||
4);
|
||||
ent->key_data[i].key_data_type[1] = KRB5_PW_SALT;
|
||||
ent->key_data[i].key_data_length[1] = 0;
|
||||
ent->key_data[i].key_data_contents[1] = NULL;
|
||||
}
|
||||
ent->key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
|
||||
ent->key_data[2].key_data_type[0] = ETYPE_DES_CBC_CRC;
|
||||
ent->n_key_data = 3;
|
||||
*mask |= KADM5_KEY_DATA;
|
||||
}
|
||||
|
||||
#ifdef EXTENDED_KADM
|
||||
if(IS_FIELD(KADM_MODDATE, vals->fields)) {
|
||||
ent->mod_date = vals->mod_date;
|
||||
*mask |= KADM5_MOD_TIME;
|
||||
}
|
||||
if(IS_FIELD(KADM_MODNAME, vals->fields)) {
|
||||
char *inst = NULL;
|
||||
if(IS_FIELD(KADM_MODINST, vals->fields))
|
||||
inst = vals->mod_instance;
|
||||
ret = krb5_425_conv_principal(context,
|
||||
vals->mod_name,
|
||||
inst,
|
||||
NULL,
|
||||
&ent->mod_name);
|
||||
if(ret)
|
||||
return ret;
|
||||
*mask |= KADM5_MOD_NAME;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
error_code(int ret)
|
||||
{
|
||||
switch(ret) {
|
||||
case KADM5_UNK_PRINC:
|
||||
return KADM_NOENTRY;
|
||||
case KADM5_DUP:
|
||||
return KADM_INUSE;
|
||||
case KADM5_AUTH_GET:
|
||||
case KADM5_AUTH_ADD:
|
||||
case KADM5_AUTH_MODIFY:
|
||||
case KADM5_AUTH_DELETE:
|
||||
case KADM5_AUTH_INSUFFICIENT:
|
||||
return KADM_UNAUTH;
|
||||
case 0:
|
||||
return 0;
|
||||
default:
|
||||
return KADM_RCSID; /* XXX */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
kadm_ser_cpw(krb5_context context,
|
||||
void *kadm_handle,
|
||||
krb5_principal principal,
|
||||
krb5_storage *message,
|
||||
krb5_storage *reply)
|
||||
{
|
||||
char key[8];
|
||||
char *password;
|
||||
krb5_error_code ret;
|
||||
|
||||
ret = message->fetch(message, key, 8);
|
||||
ret = krb5_ret_stringz(message, &password);
|
||||
|
||||
if(password)
|
||||
ret = kadm5_chpass_principal(kadm_handle, principal, password);
|
||||
else {
|
||||
krb5_key_data key_data[3];
|
||||
int i;
|
||||
for(i = 0; i < 3; i++) {
|
||||
key_data[i].key_data_ver = 2;
|
||||
key_data[i].key_data_kvno = 0;
|
||||
/* key */
|
||||
key_data[i].key_data_type[0] = ETYPE_DES_CBC_CRC;
|
||||
key_data[i].key_data_length[0] = 8;
|
||||
key_data[i].key_data_contents[0] = malloc(8);
|
||||
memcpy(key_data[i].key_data_contents[0], &key, 8);
|
||||
/* salt */
|
||||
key_data[i].key_data_type[1] = KRB5_PW_SALT;
|
||||
key_data[i].key_data_length[1] = 0;
|
||||
key_data[i].key_data_contents[1] = NULL;
|
||||
}
|
||||
key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5;
|
||||
key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
|
||||
ret = kadm5_s_chpass_principal_with_key(kadm_handle,
|
||||
principal, 3, key_data);
|
||||
}
|
||||
|
||||
if(ret != 0) {
|
||||
krb5_store_stringz(reply, (char*)krb5_get_err_text(context, ret));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kadm_ser_add(krb5_context context,
|
||||
void *kadm_handle,
|
||||
krb5_principal principal,
|
||||
krb5_storage *message,
|
||||
krb5_storage *reply)
|
||||
{
|
||||
int32_t mask;
|
||||
kadm5_principal_ent_rec ent, out;
|
||||
Kadm_vals values;
|
||||
krb5_error_code ret;
|
||||
|
||||
ret_vals(message, &values);
|
||||
|
||||
ret = values_to_ent(context, &values, &ent, &mask);
|
||||
if(ret) {
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
}
|
||||
|
||||
ret = kadm5_s_create_principal_with_key(kadm_handle, &ent, mask);
|
||||
if(ret) {
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
return error_code(ret);
|
||||
}
|
||||
|
||||
mask = KADM5_PRINCIPAL | KADM5_PW_EXPIRATION | KADM5_MAX_LIFE |
|
||||
KADM5_KEY_DATA | KADM5_MOD_TIME | KADM5_MOD_NAME;
|
||||
|
||||
kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
|
||||
ent_to_values(context, &out, mask, &values);
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
kadm5_free_principal_ent(kadm_handle, &out);
|
||||
store_vals(reply, &values);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kadm_ser_get(krb5_context context,
|
||||
void *kadm_handle,
|
||||
krb5_principal principal,
|
||||
krb5_storage *message,
|
||||
krb5_storage *reply)
|
||||
{
|
||||
Kadm_vals values;
|
||||
kadm5_principal_ent_rec ent, out;
|
||||
int32_t mask;
|
||||
char flags[FLDSZ];
|
||||
krb5_error_code ret;
|
||||
|
||||
ret_vals(message, &values);
|
||||
/* XXX BRAIN DAMAGE! these flags are not stored in the same order
|
||||
as in the header */
|
||||
krb5_ret_int8(message, &flags[3]);
|
||||
krb5_ret_int8(message, &flags[2]);
|
||||
krb5_ret_int8(message, &flags[1]);
|
||||
krb5_ret_int8(message, &flags[0]);
|
||||
ret = values_to_ent(context, &values, &ent, &mask);
|
||||
if(ret) {
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
return error_code(ret);
|
||||
}
|
||||
mask = flags_4_to_5(flags);
|
||||
|
||||
ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
|
||||
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
|
||||
ent_to_values(context, &out, mask, &values);
|
||||
|
||||
kadm5_free_principal_ent(kadm_handle, &out);
|
||||
|
||||
store_vals(reply, &values);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kadm_ser_mod(krb5_context context,
|
||||
void *kadm_handle,
|
||||
krb5_principal principal,
|
||||
krb5_storage *message,
|
||||
krb5_storage *reply)
|
||||
{
|
||||
Kadm_vals values1, values2;
|
||||
kadm5_principal_ent_rec ent, out;
|
||||
int32_t mask;
|
||||
krb5_error_code ret;
|
||||
|
||||
ret_vals(message, &values1);
|
||||
/* why are the old values sent? is the mask the same in the old and
|
||||
the new entry? */
|
||||
ret_vals(message, &values2);
|
||||
|
||||
ret = values_to_ent(context, &values2, &ent, &mask);
|
||||
if(ret) {
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
return error_code(ret);
|
||||
}
|
||||
|
||||
ret = kadm5_s_modify_principal_with_key(kadm_handle, &ent, mask);
|
||||
if(ret) {
|
||||
krb5_warn(context, ret, "kadm5_s_modify_principal");
|
||||
return error_code(ret);
|
||||
}
|
||||
|
||||
ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
|
||||
if(ret) {
|
||||
krb5_warn(context, ret, "kadm5_s_modify_principal");
|
||||
return error_code(ret);
|
||||
}
|
||||
|
||||
ent_to_values(context, &out, mask, &values1);
|
||||
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
kadm5_free_principal_ent(kadm_handle, &out);
|
||||
|
||||
store_vals(reply, &values1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
kadm_ser_del(krb5_context context,
|
||||
void *kadm_handle,
|
||||
krb5_principal principal,
|
||||
krb5_storage *message,
|
||||
krb5_storage *reply)
|
||||
{
|
||||
Kadm_vals values;
|
||||
kadm5_principal_ent_rec ent;
|
||||
int32_t mask;
|
||||
krb5_error_code ret;
|
||||
|
||||
ret_vals(message, &values);
|
||||
|
||||
ret = values_to_ent(context, &values, &ent, &mask);
|
||||
if(ret) {
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
return error_code(ret);
|
||||
}
|
||||
|
||||
ret = kadm5_delete_principal(kadm_handle, ent.principal);
|
||||
|
||||
kadm5_free_principal_ent(kadm_handle, &ent);
|
||||
|
||||
return error_code(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch(krb5_context context,
|
||||
void *kadm_handle,
|
||||
krb5_principal principal,
|
||||
krb5_data msg,
|
||||
krb5_data *reply)
|
||||
{
|
||||
int retval;
|
||||
int8_t command;
|
||||
|
||||
krb5_storage *sp_in, *sp_out;
|
||||
|
||||
sp_in = krb5_storage_from_data(&msg);
|
||||
krb5_ret_int8(sp_in, &command);
|
||||
|
||||
sp_out = krb5_storage_emem();
|
||||
sp_out->store(sp_out, KADM_VERSTR, KADM_VERSIZE);
|
||||
krb5_store_int32(sp_out, 0);
|
||||
|
||||
switch(command) {
|
||||
case CHANGE_PW:
|
||||
retval = kadm_ser_cpw(context, kadm_handle, principal,
|
||||
sp_in, sp_out);
|
||||
break;
|
||||
case ADD_ENT:
|
||||
retval = kadm_ser_add(context, kadm_handle, principal,
|
||||
sp_in, sp_out);
|
||||
break;
|
||||
case GET_ENT:
|
||||
retval = kadm_ser_get(context, kadm_handle, principal,
|
||||
sp_in, sp_out);
|
||||
break;
|
||||
case MOD_ENT:
|
||||
retval = kadm_ser_mod(context, kadm_handle, principal,
|
||||
sp_in, sp_out);
|
||||
break;
|
||||
case DEL_ENT:
|
||||
retval = kadm_ser_del(context, kadm_handle, principal,
|
||||
sp_in, sp_out);
|
||||
break;
|
||||
default:
|
||||
retval = KADM_NO_OPCODE;
|
||||
break;
|
||||
}
|
||||
krb5_storage_free(sp_in);
|
||||
if(retval) {
|
||||
krb5_storage_free(sp_out);
|
||||
make_error_packet(retval, reply);
|
||||
return;
|
||||
}
|
||||
krb5_storage_to_data(sp_out, reply);
|
||||
krb5_storage_free(sp_out);
|
||||
}
|
||||
|
||||
static void
|
||||
decode_packet(krb5_context context,
|
||||
struct sockaddr_in *admin_addr,
|
||||
struct sockaddr_in *client_addr,
|
||||
krb5_data message,
|
||||
krb5_data *reply)
|
||||
{
|
||||
int ret;
|
||||
KTEXT_ST authent;
|
||||
AUTH_DAT ad;
|
||||
MSG_DAT msg_dat;
|
||||
off_t off = 0;
|
||||
unsigned long rlen;
|
||||
char sname[] = "changepw", sinst[] = "kerberos";
|
||||
unsigned long checksum;
|
||||
des_key_schedule schedule;
|
||||
char *msg = message.data;
|
||||
|
||||
void *kadm_handle;
|
||||
|
||||
if(message.length < KADM_VERSIZE || strncmp(msg, KADM_VERSTR, KADM_VERSIZE) != 0)
|
||||
;
|
||||
off = KADM_VERSIZE;
|
||||
off += _krb5_get_int(msg + off, &rlen, 4);
|
||||
memset(&authent, 0, sizeof(authent));
|
||||
authent.length = message.length - rlen - KADM_VERSIZE - 4;
|
||||
memcpy(authent.dat, (char*)msg + off, authent.length);
|
||||
off += authent.length;
|
||||
|
||||
{
|
||||
krb5_principal principal;
|
||||
krb5_keyblock *key;
|
||||
krb5_make_principal(context, &principal, NULL,
|
||||
"changepw", "kerberos", NULL);
|
||||
ret = krb5_kt_read_service_key(context,
|
||||
NULL,
|
||||
principal,
|
||||
0,
|
||||
ETYPE_DES_CBC_CRC,
|
||||
&key);
|
||||
krb5_free_principal(context, principal);
|
||||
if(ret) {
|
||||
if(ret == KRB5_KT_NOTFOUND)
|
||||
make_error_packet(KADM_NO_AUTH, reply);
|
||||
else
|
||||
/* XXX */
|
||||
make_error_packet(KADM_NO_AUTH, reply);
|
||||
krb5_warn(context, ret, "krb5_kt_read_service_key");
|
||||
return;
|
||||
}
|
||||
|
||||
if(key->keyvalue.length != 8)
|
||||
krb5_abortx(context, "key has wrong length (%lu)",
|
||||
(unsigned long)key->keyvalue.length);
|
||||
krb_set_key(key->keyvalue.data, 0);
|
||||
krb5_free_keyblock(context, key);
|
||||
}
|
||||
|
||||
ret = krb_rd_req(&authent, sname, sinst,
|
||||
client_addr->sin_addr.s_addr, &ad, NULL);
|
||||
|
||||
if(ret) {
|
||||
make_error_packet(krb_err_base + ret, reply);
|
||||
krb5_warnx(context, "krb_rd_req: %d", ret);
|
||||
return;
|
||||
}
|
||||
{
|
||||
krb5_principal client;
|
||||
char *client_str;
|
||||
krb5_425_conv_principal(context, ad.pname, ad.pinst, ad.prealm,
|
||||
&client);
|
||||
krb5_unparse_name(context, client, &client_str);
|
||||
krb5_free_principal(context, client);
|
||||
|
||||
ret = kadm5_init_with_password_ctx(context,
|
||||
client_str,
|
||||
NULL,
|
||||
KADM5_ADMIN_SERVICE,
|
||||
NULL, 0, 0,
|
||||
&kadm_handle);
|
||||
|
||||
free(client_str);
|
||||
}
|
||||
|
||||
checksum = des_quad_cksum((des_cblock*)(msg + off), NULL, rlen,
|
||||
0, &ad.session);
|
||||
if(checksum != ad.checksum)
|
||||
;
|
||||
des_set_key(&ad.session, schedule);
|
||||
ret = krb_rd_priv(msg + off, rlen, schedule, &ad.session,
|
||||
client_addr, admin_addr, &msg_dat);
|
||||
|
||||
{
|
||||
krb5_data d, r;
|
||||
krb5_principal principal;
|
||||
d.data = msg_dat.app_data;
|
||||
d.length = msg_dat.app_length;
|
||||
|
||||
krb5_425_conv_principal(context,
|
||||
ad.pname, ad.pinst, ad.prealm,
|
||||
&principal);
|
||||
|
||||
dispatch(context, kadm_handle, principal, d, &r);
|
||||
krb5_data_alloc(reply, r.length + 26);
|
||||
reply->length = krb_mk_priv(r.data, reply->data, r.length,
|
||||
schedule, &ad.session,
|
||||
admin_addr, client_addr);
|
||||
if((ssize_t)reply->length < 0) {
|
||||
make_error_packet(KADM_NO_ENCRYPT, reply);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
handle_v4(krb5_context context,
|
||||
int len,
|
||||
int fd);
|
||||
|
||||
void
|
||||
handle_v4(krb5_context context,
|
||||
int len,
|
||||
int fd)
|
||||
{
|
||||
int first = 1;
|
||||
struct sockaddr_in admin_addr, client_addr;
|
||||
int addr_len;
|
||||
krb5_error_code ret;
|
||||
krb5_data message, reply;
|
||||
|
||||
addr_len = sizeof(client_addr);
|
||||
getsockname(fd, (struct sockaddr*)&admin_addr, &addr_len);
|
||||
addr_len = sizeof(client_addr);
|
||||
getpeername(fd, (struct sockaddr*)&client_addr, &addr_len);
|
||||
|
||||
while(1) {
|
||||
if(first) {
|
||||
/* first time around, we have already read len, and two
|
||||
bytes of the version string */
|
||||
krb5_data_alloc(&message, len);
|
||||
memcpy(message.data, "KA", 2);
|
||||
ret = krb5_net_read(context, &fd, (char*)message.data + 2, len - 2);
|
||||
first = 0;
|
||||
} else {
|
||||
char buf[2];
|
||||
unsigned long tmp;
|
||||
ret = krb5_net_read(context, &fd, buf, sizeof(2));
|
||||
if(ret == 0)
|
||||
exit(0);
|
||||
_krb5_get_int(buf, &tmp, 2);
|
||||
krb5_data_alloc(&message, tmp);
|
||||
krb5_net_read(context, &fd, message.data, message.length);
|
||||
}
|
||||
decode_packet(context, &admin_addr, &client_addr,
|
||||
message, &reply);
|
||||
krb5_data_free(&message);
|
||||
{
|
||||
char buf[2];
|
||||
_krb5_put_int(buf, reply.length, sizeof(buf));
|
||||
krb5_net_write(context, &fd, buf, sizeof(buf));
|
||||
krb5_net_write(context, &fd, reply.data, reply.length);
|
||||
krb5_data_free(&reply);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user