Logo Search packages:      
Sourcecode: heimdal version File versions

version4.c

/*
 * Copyright (c) 1999 - 2002 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: version4.c,v 1.29.2.1 2004/04/29 12:29:23 lha Exp $");

#define KADM_NO_OPCODE -1
#define KADM_NO_ENCRYPT -2

/*
 * make an error packet if we fail encrypting
 */

static void
make_you_lose_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 krb5_storage_read(sp, fields, FLDSZ);
}

static int
store_fields(krb5_storage *sp, char *fields)
{
    return krb5_storage_write(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);
            strlcpy(vals->name, tmp_string, sizeof(vals->name));
            free(tmp_string);
            break;
          case KADM_INST:
            krb5_ret_stringz(sp, &tmp_string);
            strlcpy(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);
            strlcpy(vals->mod_name, tmp_string, 
                        sizeof(vals->mod_name));
            free(tmp_string);
            break;
          case KADM_MODINST:
            krb5_ret_stringz(sp, &tmp_string);
            strlcpy(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_PRINC_EXPIRE_TIME;
          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];
    time_t exp = 0;

    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_PRINC_EXPIRE_TIME) {
      if(ent->princ_expire_time != 0)
          exp = ent->princ_expire_time;
    }
    if(mask & KADM5_PW_EXPIRATION) {
      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
}

/*
 * convert the kadm4 values in `vals' to `ent' (and `mask')
 */

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->princ_expire_time = vals->exp_date;
      *mask |= KADM5_PRINC_EXPIRE_TIME;
    }
    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++) {
          u_int32_t key_low, key_high;

          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;

          key_low  = ntohl(vals->key_low);
          key_high = ntohl(vals->key_high);
          memcpy(ent->key_data[i].key_data_contents[0],
               &key_low, 4);
          memcpy((char*)ent->key_data[i].key_data_contents[0] + 4,
               &key_high, 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;
}

/*
 * Try to translate a KADM5 error code into a v4 kadmin one.
 */

static int
error_code(int ret)
{
    switch (ret) {
    case 0:
      return 0;
    case KADM5_FAILURE :
    case KADM5_AUTH_GET :
    case KADM5_AUTH_ADD :
    case KADM5_AUTH_MODIFY :
    case KADM5_AUTH_DELETE :
    case KADM5_AUTH_INSUFFICIENT :
      return KADM_UNAUTH;
    case KADM5_BAD_DB :
      return KADM_UK_RERROR;
    case KADM5_DUP :
      return KADM_INUSE;
    case KADM5_RPC_ERROR :
    case KADM5_NO_SRV :
      return KADM_NO_SERV;
    case KADM5_NOT_INIT :
      return KADM_NO_CONN;
    case KADM5_UNK_PRINC :
      return KADM_NOENTRY;
    case KADM5_PASS_Q_TOOSHORT :
#ifdef KADM_PASS_Q_TOOSHORT
      return KADM_PASS_Q_TOOSHORT;
#else
      return KADM_INSECURE_PW;
#endif
    case KADM5_PASS_Q_CLASS :
#ifdef KADM_PASS_Q_CLASS
      return KADM_PASS_Q_CLASS;
#else
      return KADM_INSECURE_PW;
#endif
    case KADM5_PASS_Q_DICT :
#ifdef KADM_PASS_Q_DICT
      return KADM_PASS_Q_DICT;
#else
      return KADM_INSECURE_PW;
#endif
    case KADM5_PASS_REUSE :
    case KADM5_PASS_TOOSOON :
    case KADM5_BAD_PASSWORD :
      return KADM_INSECURE_PW;
    case KADM5_PROTECT_PRINCIPAL :
      return KADM_IMMUTABLE;
    case KADM5_POLICY_REF :
    case KADM5_INIT :
    case KADM5_BAD_HIST_KEY :
    case KADM5_UNK_POLICY :
    case KADM5_BAD_MASK :
    case KADM5_BAD_CLASS :
    case KADM5_BAD_LENGTH :
    case KADM5_BAD_POLICY :
    case KADM5_BAD_PRINCIPAL :
    case KADM5_BAD_AUX_ATTR :
    case KADM5_BAD_HISTORY :
    case KADM5_BAD_MIN_PASS_LIFE :
    case KADM5_BAD_SERVER_HANDLE :
    case KADM5_BAD_STRUCT_VERSION :
    case KADM5_OLD_STRUCT_VERSION :
    case KADM5_NEW_STRUCT_VERSION :
    case KADM5_BAD_API_VERSION :
    case KADM5_OLD_LIB_API_VERSION :
    case KADM5_OLD_SERVER_API_VERSION :
    case KADM5_NEW_LIB_API_VERSION :
    case KADM5_NEW_SERVER_API_VERSION :
    case KADM5_SECURE_PRINC_MISSING :
    case KADM5_NO_RENAME_SALT :
    case KADM5_BAD_CLIENT_PARAMS :
    case KADM5_BAD_SERVER_PARAMS :
    case KADM5_AUTH_LIST :
    case KADM5_AUTH_CHANGEPW :
    case KADM5_BAD_TL_TYPE :
    case KADM5_MISSING_CONF_PARAMS :
    case KADM5_BAD_SERVER_NAME :
    default :
      return KADM_UNAUTH;     /* XXX */
    }
}

/*
 * server functions
 */

static int
kadm_ser_cpw(krb5_context context,
           void *kadm_handle, 
           krb5_principal principal, 
           const char *principal_string,
           krb5_storage *message,
           krb5_storage *reply)
{
    char key[8];
    char *password = NULL;
    krb5_error_code ret;
    
    krb5_warnx(context, "v4-compat %s: CHPASS %s",
             principal_string, principal_string); 

    ret = krb5_storage_read(message, key + 4, 4);
    ret = krb5_storage_read(message, key, 4);
    ret = krb5_ret_stringz(message, &password);
    
    if(password) {
      krb5_data pwd_data;
      const char *tmp;

      pwd_data.data   = password;
      pwd_data.length = strlen(password);

      tmp = kadm5_check_password_quality (context, principal, &pwd_data);

      if (tmp != NULL) {
          krb5_store_stringz (reply, (char *)tmp);
          ret = KADM5_PASS_Q_DICT;
          goto fail;
      }
      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));
      goto fail;
    }
    return 0;
fail:
    krb5_warn(context, ret, "v4-compat CHPASS");
    return error_code(ret);
}

static int
kadm_ser_add(krb5_context context,
           void *kadm_handle, 
           krb5_principal principal, 
           const char *principal_string,
           krb5_storage *message,
           krb5_storage *reply)
{
    int32_t mask;
    kadm5_principal_ent_rec ent, out;
    Kadm_vals values;
    krb5_error_code ret;
    char name[128];

    ret_vals(message, &values);

    ret = values_to_ent(context, &values, &ent, &mask);
    if(ret)
      goto fail;

    krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
    krb5_warnx(context, "v4-compat %s: ADD %s",
             principal_string, name);

    ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_ADD,
                               ent.principal);
    if (ret)
      goto fail;

    ret = kadm5_s_create_principal_with_key(kadm_handle, &ent, mask);
    if(ret) {
      kadm5_free_principal_ent(kadm_handle, &ent);
      goto fail;
    }

    mask = KADM5_PRINCIPAL | KADM5_PRINC_EXPIRE_TIME | 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;
fail:
    krb5_warn(context, ret, "v4-compat ADD");
    return error_code(ret);
}

static int
kadm_ser_get(krb5_context context,
           void *kadm_handle, 
           krb5_principal principal, 
           const char *principal_string,
           krb5_storage *message,
           krb5_storage *reply)
{
    krb5_error_code ret;
    Kadm_vals values;
    kadm5_principal_ent_rec ent, out;
    int32_t mask;
    char flags[FLDSZ];
    char name[128];

    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)
      goto fail;

    krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
    krb5_warnx(context, "v4-compat %s: GET %s",
             principal_string, name);

    ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_GET,
                               ent.principal);
    if (ret)
      goto fail;

    mask = flags_4_to_5(flags);

    ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
    kadm5_free_principal_ent(kadm_handle, &ent);

    if (ret)
      goto fail;
    
    ent_to_values(context, &out, mask, &values);

    kadm5_free_principal_ent(kadm_handle, &out);

    store_vals(reply, &values);
    return 0;
fail:
    krb5_warn(context, ret, "v4-compat GET");
    return error_code(ret);
}

static int
kadm_ser_mod(krb5_context context,
           void *kadm_handle, 
           krb5_principal principal, 
           const char *principal_string,
           krb5_storage *message,
           krb5_storage *reply)
{
    Kadm_vals values1, values2;
    kadm5_principal_ent_rec ent, out;
    int32_t mask;
    krb5_error_code ret;
    char name[128];
    
    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)
      goto fail;

    krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
    krb5_warnx(context, "v4-compat %s: MOD %s",
             principal_string, name);

    ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_MODIFY,
                               ent.principal);
    if (ret)
      goto fail;

    ret = kadm5_s_modify_principal(kadm_handle, &ent, mask);
    if(ret) {
      kadm5_free_principal_ent(kadm_handle, &ent);
      krb5_warn(context, ret, "kadm5_s_modify_principal");
      goto fail;
    }

    ret = kadm5_get_principal(kadm_handle, ent.principal, &out, mask);
    if(ret) {
      kadm5_free_principal_ent(kadm_handle, &ent);
      krb5_warn(context, ret, "kadm5_s_modify_principal");
      goto fail;
    }

    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;
fail:
    krb5_warn(context, ret, "v4-compat MOD");
    return error_code(ret);
}

static int
kadm_ser_del(krb5_context context,
           void *kadm_handle, 
           krb5_principal principal, 
           const char *principal_string,
           krb5_storage *message,
           krb5_storage *reply)
{
    Kadm_vals values;
    kadm5_principal_ent_rec ent;
    int32_t mask;
    krb5_error_code ret;
    char name[128];

    ret_vals(message, &values);

    ret = values_to_ent(context, &values, &ent, &mask);
    if(ret)
      goto fail;
    
    krb5_unparse_name_fixed(context, ent.principal, name, sizeof(name));
    krb5_warnx(context, "v4-compat %s: DEL %s",
             principal_string, name);

    ret = _kadm5_acl_check_permission (kadm_handle, KADM5_PRIV_DELETE,
                               ent.principal);
    if (ret)
      goto fail;

    ret = kadm5_delete_principal(kadm_handle, ent.principal);

    kadm5_free_principal_ent(kadm_handle, &ent);

    if (ret)
      goto fail;

    return 0;
fail:
    krb5_warn(context, ret, "v4-compat ADD");
    return error_code(ret);
}

static int
dispatch(krb5_context context,
       void *kadm_handle,
       krb5_principal principal,
       const char *principal_string,
       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();
    krb5_storage_write(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,
                        principal_string,
                        sp_in, sp_out);
      break;
    case ADD_ENT:
      retval = kadm_ser_add(context, kadm_handle, principal,
                        principal_string,
                        sp_in, sp_out);
      break;
    case GET_ENT:
      retval = kadm_ser_get(context, kadm_handle, principal,
                        principal_string,
                        sp_in, sp_out);
      break;
    case MOD_ENT:
      retval = kadm_ser_mod(context, kadm_handle, principal,
                        principal_string,
                        sp_in, sp_out);
      break;
    case DEL_ENT:
      retval = kadm_ser_del(context, kadm_handle, principal,
                        principal_string,
                        sp_in, sp_out);
      break;
    default:
      krb5_warnx(context, "v4-compat %s: unknown opcode: %d",
               principal_string, command);
      retval = KADM_NO_OPCODE;
      break;
    }
    krb5_storage_free(sp_in);
    if(retval) {
      krb5_storage_seek(sp_out, KADM_VERSIZE, SEEK_SET);
      krb5_store_int32(sp_out, retval);
    }
    krb5_storage_to_data(sp_out, reply);
    krb5_storage_free(sp_out);
    return retval;
}

/*
 * Decode a v4 kadmin packet in `message' and create a reply in `reply'
 */

static void
decode_packet(krb5_context context,
            krb5_keytab keytab,
            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;
    krb5_principal client;
    char *client_str;
    krb5_keytab_entry entry;
    
    if(message.length < KADM_VERSIZE + 4
       || strncmp(msg, KADM_VERSTR, KADM_VERSIZE) != 0) {
      make_you_lose_packet (KADM_BAD_VER, reply);
      return;
    }

    off = KADM_VERSIZE;
    off += _krb5_get_int(msg + off, &rlen, 4);
    memset(&authent, 0, sizeof(authent));
    authent.length = message.length - rlen - KADM_VERSIZE - 4;

    if(rlen > message.length - KADM_VERSIZE - 4
       || authent.length > MAX_KTXT_LEN) {
      krb5_warnx(context, "received bad rlen (%lu)", (unsigned long)rlen);
      make_you_lose_packet (KADM_LENGTH_ERROR, reply);
      return;
    }

    memcpy(authent.dat, (char*)msg + off, authent.length);
    off += authent.length;
    
    {
      krb5_principal principal;
      krb5_keyblock *key;

      ret = krb5_make_principal(context, &principal, NULL, 
                          "changepw", "kerberos", NULL);
      if (ret) {
          krb5_warn (context, ret, "krb5_make_principal");
          make_you_lose_packet (KADM_NOMEM, reply);
          return;
      }
      ret = krb5_kt_get_entry (context, keytab, principal, 0,
                         ETYPE_DES_CBC_MD5, &entry);
      krb5_kt_close (context, keytab);
      if (ret) {
          krb5_free_principal(context, principal);
          make_you_lose_packet (KADM_NO_AUTH, reply);
          return;
      }
      ret = krb5_copy_keyblock (context, &entry.keyblock,& key);
      krb5_kt_free_entry(context, &entry);
      krb5_free_principal(context, principal);
      if(ret) {
          if(ret == KRB5_KT_NOTFOUND)
            make_you_lose_packet(KADM_NO_AUTH, reply);
          else
            /* XXX */
            make_you_lose_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_you_lose_packet(ERROR_TABLE_BASE_krb + ret, reply);
      krb5_warnx(context, "krb_rd_req: %d", ret);
      return;
    }

    ret = krb5_425_conv_principal(context, ad.pname, ad.pinst, ad.prealm,
                          &client);
    if (ret) {
      krb5_warnx (context, "krb5_425_conv_principal: %d", ret);
      make_you_lose_packet (KADM_NOMEM, reply);
      return;
    }

    krb5_unparse_name(context, client, &client_str);

    ret = kadm5_init_with_password_ctx(context, 
                               client_str, 
                               NULL,
                               KADM5_ADMIN_SERVICE,
                               NULL, 0, 0, 
                               &kadm_handle);
    if (ret) {
      krb5_warn (context, ret, "kadm5_init_with_password_ctx");
      make_you_lose_packet (KADM_NOMEM, reply);
      goto out;
    }
    
    checksum = des_quad_cksum((void *)(msg + off), NULL, rlen, 0, &ad.session);
    if(checksum != ad.checksum) {
      krb5_warnx(context, "decode_packet: bad checksum");
      make_you_lose_packet (KADM_BAD_CHK, reply);
      goto out;
    }
    des_set_key(&ad.session, schedule);
    ret = krb_rd_priv(msg + off, rlen, schedule, &ad.session, 
                  client_addr, admin_addr, &msg_dat);
    if (ret) {
      make_you_lose_packet (ERROR_TABLE_BASE_krb + ret, reply);
      krb5_warnx(context, "krb_rd_priv: %d", ret);
      goto out;
    }

    {
      krb5_data d, r;
      int retval;

      d.data   = msg_dat.app_data;
      d.length = msg_dat.app_length;
      
      retval = dispatch(context, kadm_handle,
                    client, client_str, 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_you_lose_packet(KADM_NO_ENCRYPT, reply);
          goto out;
      }
    }
out:
    krb5_free_principal(context, client);
    free(client_str);
}

void
handle_v4(krb5_context context,
        krb5_keytab keytab,
        int len,
        int fd)
{
    int first = 1;
    struct sockaddr_in admin_addr, client_addr;
    socklen_t addr_len;
    krb5_data message, reply;
    ssize_t n;

    addr_len = sizeof(client_addr);
    if (getsockname(fd, (struct sockaddr*)&admin_addr, &addr_len) < 0)
      krb5_errx (context, 1, "getsockname");
    addr_len = sizeof(client_addr);
    if (getpeername(fd, (struct sockaddr*)&client_addr, &addr_len) < 0)
      krb5_errx (context, 1, "getpeername");

    while(1) {
      doing_useful_work = 0;
      if(term_flag)
          exit(0);
      if(first) {
          if (len < 2)
            krb5_errx(context, 1, "received too short len (%d < 2)", len);
          /* 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);
          n = krb5_net_read(context, &fd, (char*)message.data + 2,
                        len - 2);
          if (n == 0)
            exit (0);
          if (n < 0)
            krb5_err (context, 1, errno, "krb5_net_read");
          first = 0;
      } else {
          char buf[2];
          unsigned long tmp;
          ssize_t n;

          n = krb5_net_read(context, &fd, buf, sizeof(2));
          if (n == 0)
            exit (0);
          if (n < 0)
            krb5_err (context, 1, errno, "krb5_net_read");
          _krb5_get_int(buf, &tmp, 2);
          krb5_data_alloc(&message, tmp);
          n = krb5_net_read(context, &fd, message.data, message.length);
          if (n == 0)
            krb5_errx (context, 1, "EOF in krb5_net_read");
          if (n < 0)
            krb5_err (context, 1, errno, "krb5_net_read");
      }
      doing_useful_work = 1;
      decode_packet(context, keytab, &admin_addr, &client_addr, 
                  message, &reply);
      krb5_data_free(&message);
      {
          char buf[2];

          _krb5_put_int(buf, reply.length, sizeof(buf));
          n = krb5_net_write(context, &fd, buf, sizeof(buf));
          if (n < 0)
            krb5_err (context, 1, errno, "krb5_net_write");
          n = krb5_net_write(context, &fd, reply.data, reply.length);
          if (n < 0)
            krb5_err (context, 1, errno, "krb5_net_write");
          krb5_data_free(&reply);
      }
    }
}

Generated by  Doxygen 1.6.0   Back to index