/ kadmin / rpc.c
rpc.c
   1  /*
   2   * Copyright (c) 2008 Kungliga Tekniska Högskolan
   3   * (Royal Institute of Technology, Stockholm, Sweden).
   4   * All rights reserved.
   5   *
   6   * Redistribution and use in source and binary forms, with or without
   7   * modification, are permitted provided that the following conditions
   8   * are met:
   9   *
  10   * 1. Redistributions of source code must retain the above copyright
  11   *    notice, this list of conditions and the following disclaimer.
  12   *
  13   * 2. Redistributions in binary form must reproduce the above copyright
  14   *    notice, this list of conditions and the following disclaimer in the
  15   *    documentation and/or other materials provided with the distribution.
  16   *
  17   * 3. Neither the name of the Institute nor the names of its contributors
  18   *    may be used to endorse or promote products derived from this software
  19   *    without specific prior written permission.
  20   *
  21   * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24   * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  25   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31   * SUCH DAMAGE.
  32   */
  33  
  34  #include "kadmin_locl.h"
  35  #include <kadm5/private.h>
  36  
  37  #include <gssapi.h>
  38  #include <gssapi_krb5.h>
  39  #include <gssapi_spnego.h>
  40  
  41  #define CHECK(x)						\
  42      do {							\
  43  	int __r;						\
  44  	if ((__r = (x))) {					\
  45  	    krb5_errx(dcontext, 1, "Failed (%d) on %s:%d",	\
  46  		      __r, __FILE__, __LINE__);			\
  47  	}							\
  48      } while(0)
  49  
  50  #define EXPECT(x,expected)						\
  51      do {								\
  52  	if ((x) != (expected)) {					\
  53  	    krb5_errx(dcontext, 1,					\
  54  		      "Got %d, was not the expected %d at %s:%d",	\
  55  		      x, expected, __FILE__, __LINE__);			\
  56  	}								\
  57      } while(0)
  58  
  59  #define EXPECT_EGT(x,expected)				\
  60      do {						\
  61  	if ((x) < (expected)) {				\
  62  	    krb5_errx(dcontext, 1,			\
  63  		      "Got %d that is < %d at %s:%d",	\
  64  		      x, expected, __FILE__, __LINE__);	\
  65  	}						\
  66      } while(0)
  67  
  68  static krb5_context dcontext;
  69  
  70  #define INSIST(x) CHECK(!(x))
  71  
  72  #define VERSION2 0x12345702
  73  
  74  #define LAST_FRAGMENT 0x80000000
  75  
  76  #define RPC_VERSION 2
  77  #define KADM_SERVER 2112
  78  #define VVERSION 2
  79  #define FLAVOR_GSS 6
  80  #define 	FLAVOR_GSS_VERSION 1
  81  #define 	SEQ_WINDOW_SIZE		1
  82  #define FLAVOR_GSS_OLD 300001
  83  #define 	FLAVOR_GSS_OLD_MIN_VERSION 3
  84  
  85  enum {
  86      RPG_DATA = 0,
  87      RPG_INIT = 1,
  88      RPG_CONTINUE_INIT = 2,
  89      RPG_DESTROY = 3
  90  };
  91  
  92  enum {
  93      rpg_privacy = 3
  94  };
  95  
  96  /*
  97    struct chrand_ret {
  98    krb5_ui_4 api_version;
  99    kadm5_ret_t ret;
 100    int n_keys;
 101    krb5_keyblock *keys;
 102    };
 103  */
 104  
 105  
 106  static int
 107  parse_name(const unsigned char *p, size_t len,
 108  	   const gss_OID oid, char **name)
 109  {
 110      size_t l;
 111      
 112      if (len < 4)
 113  	return 1;
 114      
 115      /* TOK_ID */
 116      if (memcmp(p, "\x04\x01", 2) != 0)
 117  	return 1;
 118      len -= 2;
 119      p += 2;
 120      
 121      /* MECH_LEN */
 122      l = (p[0] << 8) | p[1];
 123      len -= 2;
 124      p += 2;
 125      if (l < 2 || len < l)
 126  	return 1;
 127      
 128      /* oid wrapping */
 129      if (p[0] != 6 || p[1] != l - 2)
 130  	return 1;
 131      p += 2;
 132      l -= 2;
 133      len -= 2;
 134      
 135      /* MECH */
 136      if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
 137  	return 1;
 138      len -= l;
 139      p += l;
 140      
 141      /* MECHNAME_LEN */
 142      if (len < 4)
 143  	return 1;
 144      l = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
 145      len -= 4;
 146      p += 4;
 147      
 148      /* MECH NAME */
 149      if (len != l)
 150  	return 1;
 151      
 152      *name = malloc(l + 1);
 153      INSIST(*name != NULL);
 154      memcpy(*name, p, l);
 155      (*name)[l] = '\0';
 156  
 157      return 0;
 158  }
 159  
 160  
 161  
 162  static void
 163  gss_error(krb5_context lcontext,
 164  	  gss_OID mech, OM_uint32 type, OM_uint32 error)
 165  {
 166      OM_uint32 new_stat;
 167      OM_uint32 msg_ctx = 0;
 168      gss_buffer_desc status_string;
 169      OM_uint32 ret;
 170  
 171      do {
 172  	ret = gss_display_status (&new_stat,
 173  				  error,
 174  				  type,
 175  				  mech,
 176  				  &msg_ctx,
 177  				  &status_string);
 178  	krb5_warnx(lcontext, "%.*s",
 179  		   (int)status_string.length,
 180  		   (char *)status_string.value);
 181  	gss_release_buffer (&new_stat, &status_string);
 182      } while (!GSS_ERROR(ret) && msg_ctx != 0);
 183  }
 184  
 185  static void
 186  gss_print_errors (krb5_context lcontext, 
 187  		  OM_uint32 maj_stat, OM_uint32 min_stat)
 188  {
 189      gss_error(lcontext, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
 190      gss_error(lcontext, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
 191  }
 192  
 193  static int
 194  read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
 195  {
 196      char buf[1024];
 197  
 198      while (len) {
 199  	size_t tlen = len;
 200  	ssize_t slen;
 201  
 202  	if (tlen > sizeof(buf))
 203  	    tlen = sizeof(buf);
 204  	
 205  	slen = krb5_storage_read(sp, buf, tlen);
 206  	INSIST((size_t)slen == tlen);
 207  	
 208  	slen = krb5_storage_write(msg, buf, tlen);
 209  	INSIST((size_t)slen == tlen);
 210  	
 211  	len -= tlen;
 212      }
 213      return 0;
 214  }
 215  
 216  static int
 217  collect_fragments(krb5_storage *sp, krb5_storage *msg)
 218  {
 219      krb5_error_code ret;
 220      uint32_t len;
 221      int last_fragment;
 222      size_t total_len = 0;
 223  
 224      do {
 225  	ret = krb5_ret_uint32(sp, &len);
 226  	if (ret)
 227  	    return ret;
 228  	
 229  	last_fragment = (len & LAST_FRAGMENT);
 230  	len &= ~LAST_FRAGMENT;
 231  
 232  	CHECK(read_data(sp, msg, len));
 233  	total_len += len;
 234  
 235      } while(!last_fragment || total_len == 0);
 236  
 237      return 0;
 238  }
 239  
 240  /*
 241   *
 242   */
 243  
 244  static void
 245  proc_create_principal(kadm5_server_context *lcontext,
 246  		      krb5_storage *in,
 247  		      krb5_storage *out)
 248  {
 249      uint32_t version, mask;
 250      kadm5_principal_ent_rec ent;
 251      krb5_error_code ret;
 252      char *password, *princ = NULL;
 253  
 254      memset(&ent, 0, sizeof(ent));
 255  
 256      CHECK(krb5_ret_uint32(in, &version));
 257      EXPECT_EGT(version, VERSION2);
 258      CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent));
 259      INSIST(ent.principal != NULL);
 260      CHECK(krb5_unparse_name(lcontext->context, ent.principal, &princ));
 261      CHECK(krb5_ret_uint32(in, &mask));
 262      CHECK(_kadm5_xdr_ret_string_xdr(in, &password));
 263  
 264      INSIST(ent.principal);
 265  
 266  
 267      ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_ADD, ent.principal);
 268      if (ret)
 269  	goto fail;
 270  
 271      ret = kadm5_create_principal(lcontext, &ent, mask, password);
 272  
 273   fail:
 274      krb5_warn(lcontext->context, ret, "create principal: %s", princ ? princ : "<noprinc>");
 275      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 276      CHECK(krb5_store_uint32(out, ret)); /* code */
 277  
 278      free(password);
 279      free(princ);
 280      kadm5_free_principal_ent(context, &ent);
 281  }
 282  
 283  /* get array of salt tuples */
 284  
 285  static krb5_key_salt_tuple *
 286  parse_ks_tuple(krb5_context lcontext, krb5_storage *in, uint32_t *n_ks_tuple)
 287  {
 288      krb5_key_salt_tuple *tuples;
 289      uint32_t n;
 290  
 291      CHECK(krb5_ret_uint32(in, n_ks_tuple));
 292      INSIST(*n_ks_tuple < 1000);
 293  
 294      if (*n_ks_tuple == 0)
 295  	return NULL;
 296  
 297      tuples = calloc(*n_ks_tuple, sizeof(tuples[0]));
 298      INSIST(tuples != NULL);
 299  
 300      for (n = 0; n < *n_ks_tuple; n++) {
 301  	int32_t enctype, salttype;
 302  	CHECK(krb5_ret_int32(in, &enctype));
 303  	CHECK(krb5_ret_int32(in, &salttype));
 304  
 305  	tuples[n].ks_enctype = (krb5_enctype)enctype;
 306  	tuples[n].ks_salttype = (krb5_enctype)salttype;
 307      }
 308      return tuples;
 309  }
 310  
 311  static void
 312  proc_create_principal3(kadm5_server_context *lcontext,
 313  		       krb5_storage *in,
 314  		       krb5_storage *out)
 315  {
 316      uint32_t version, mask, n_ks_tuple;
 317      kadm5_principal_ent_rec ent;
 318      krb5_error_code ret;
 319      char *password, *princ = NULL;
 320      krb5_key_salt_tuple *ks_tuple;
 321  
 322      memset(&ent, 0, sizeof(ent));
 323  
 324      CHECK(krb5_ret_uint32(in, &version));
 325      EXPECT_EGT(version, VERSION2);
 326      CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent));
 327      INSIST(ent.principal != NULL);
 328      CHECK(krb5_unparse_name(lcontext->context, ent.principal, &princ));
 329      CHECK(krb5_ret_uint32(in, &mask));
 330      ks_tuple = parse_ks_tuple(lcontext->context, in, &n_ks_tuple);
 331      CHECK(_kadm5_xdr_ret_string_xdr(in, &password));
 332  
 333      INSIST(ent.principal);
 334  
 335      ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_ADD, ent.principal);
 336      if (ret)
 337  	goto fail;
 338  
 339      ret = kadm5_create_principal_2(context, &ent, mask, n_ks_tuple, ks_tuple, password);
 340  
 341   fail:
 342      krb5_warn(lcontext->context, ret, "create principal: %s", princ ? princ : "<noprinc>");
 343      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 344      CHECK(krb5_store_uint32(out, ret)); /* code */
 345  
 346      free(password);
 347      kadm5_free_principal_ent(lcontext, &ent);
 348  
 349      free(princ);
 350      if (ks_tuple)
 351  	free(ks_tuple);
 352      kadm5_free_principal_ent(context, &ent);
 353  }
 354  
 355  static void
 356  proc_delete_principal(kadm5_server_context *lcontext,
 357  		      krb5_storage *in,
 358  		      krb5_storage *out)
 359  {
 360      uint32_t version;
 361      krb5_principal principal;
 362      krb5_error_code ret;
 363      char *princ = NULL;
 364  
 365      CHECK(krb5_ret_uint32(in, &version));
 366      EXPECT_EGT(version, VERSION2);
 367      CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
 368      CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
 369  
 370      ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_DELETE, principal);
 371      if (ret)
 372  	goto fail;
 373  
 374      ret = kadm5_delete_principal(lcontext, principal);
 375  
 376   fail:
 377      krb5_warn(lcontext->context, ret, "delete principal: %s", princ ? princ : "<noprinc>");
 378      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 379      CHECK(krb5_store_uint32(out, ret)); /* code */
 380  
 381      free(princ);
 382      krb5_free_principal(lcontext->context, principal);
 383  }
 384  
 385  static void
 386  proc_modify_principal(kadm5_server_context *lcontext,
 387  		      krb5_storage *in,
 388  		      krb5_storage *out)
 389  {
 390      uint32_t version, mask;
 391      kadm5_principal_ent_rec ent;
 392      krb5_error_code ret;
 393  
 394      CHECK(krb5_ret_uint32(in, &version));
 395      EXPECT_EGT(version, VERSION2);
 396      CHECK(_kadm5_xdr_ret_principal_ent(lcontext->context, in, &ent));
 397      INSIST(ent.principal != NULL);
 398      CHECK(krb5_ret_uint32(in, &mask));
 399  
 400      ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_MODIFY,
 401  				      ent.principal);
 402      if (ret)
 403  	goto fail;
 404  
 405      ret = kadm5_modify_principal(lcontext, &ent, mask);
 406  
 407   fail:
 408      krb5_warn(lcontext->context, ret, "modify principal");
 409      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 410      CHECK(krb5_store_uint32(out, ret)); /* code */
 411  
 412      kadm5_free_principal_ent(lcontext, &ent);
 413  }
 414  
 415  static void
 416  proc_get_principal(kadm5_server_context *lcontext,
 417  		   krb5_storage *in,
 418  		   krb5_storage *out)
 419  {
 420      uint32_t version, mask;
 421      krb5_principal principal;
 422      kadm5_principal_ent_rec ent;
 423      krb5_error_code ret;
 424      char *princ = NULL;
 425  
 426      memset(&ent, 0, sizeof(ent));
 427  
 428      CHECK(krb5_ret_uint32(in, &version));
 429      EXPECT_EGT(version, VERSION2);
 430      CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
 431      CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
 432      CHECK(krb5_ret_uint32(in, &mask));
 433  
 434      ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_GET, principal);
 435      if(ret)
 436  	goto fail;
 437  
 438      mask |= KADM5_KVNO | KADM5_PRINCIPAL;
 439  
 440      ret = kadm5_get_principal(lcontext, principal, &ent, mask);
 441  
 442   fail:
 443      krb5_warn(lcontext->context, ret, "get principal: %s kvno %d",
 444  	      princ ? princ : "<unknown>", (int)ent.kvno);
 445  
 446      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 447      CHECK(krb5_store_uint32(out, ret)); /* code */
 448      if (ret == 0) {
 449  	CHECK(_kadm5_xdr_store_principal_ent(lcontext->context, out, &ent));
 450      }
 451      krb5_free_principal(lcontext->context, principal);
 452      kadm5_free_principal_ent(lcontext, &ent);
 453  }
 454  
 455  static void
 456  proc_chrand_principal_v2(kadm5_server_context *lcontext,
 457  			 krb5_storage *in, 
 458  			 krb5_storage *out)
 459  {
 460      krb5_error_code ret;
 461      krb5_principal principal;
 462      uint32_t version;
 463      krb5_keyblock *new_keys = NULL;
 464      int n_keys = 0;
 465      char *princ = NULL;
 466  
 467      CHECK(krb5_ret_uint32(in, &version));
 468      EXPECT_EGT(version, VERSION2);
 469      CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
 470      CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
 471  
 472      ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_CPW, principal);
 473      if(ret)
 474  	goto fail;
 475  
 476      ret = kadm5_randkey_principal(lcontext, principal,
 477  				  &new_keys, &n_keys);
 478  
 479   fail:
 480      krb5_warn(lcontext->context, ret, "rand key principal v2: %s",
 481  	      princ ? princ : "<unknown>");
 482  
 483      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 484      CHECK(krb5_store_uint32(out, ret));
 485      if (ret == 0) {
 486  	size_t i;
 487  	CHECK(krb5_store_int32(out, n_keys));
 488  
 489  	for(i = 0; i < (size_t)n_keys; i++){
 490  	    CHECK(krb5_store_uint32(out, new_keys[i].keytype));
 491  	    CHECK(_kadm5_xdr_store_data_xdr(out, new_keys[i].keyvalue));
 492  	    krb5_free_keyblock_contents(lcontext->context, &new_keys[i]);
 493  	}
 494  	free(new_keys);
 495      }
 496      krb5_free_principal(lcontext->context, principal);
 497      if (princ)
 498  	free(princ);
 499  }
 500  
 501  static void
 502  proc_chrand_principal_v3(kadm5_server_context *lcontext,
 503  			 krb5_storage *in, 
 504  			 krb5_storage *out)
 505  {
 506      krb5_error_code ret;
 507      krb5_principal principal;
 508      uint32_t version, keepold, n_ks_tuple;
 509      krb5_keyblock *new_keys = NULL;
 510      krb5_key_salt_tuple *ks_tuple = NULL;
 511      char *princ = NULL;
 512      int n_keys = 0;
 513  
 514      CHECK(krb5_ret_uint32(in, &version));
 515      EXPECT_EGT(version, VERSION2);
 516      CHECK(_kadm5_xdr_ret_principal_xdr(lcontext->context, in, &principal));
 517      CHECK(krb5_unparse_name(lcontext->context, principal, &princ));
 518      CHECK(krb5_ret_uint32(in, &keepold));
 519      ks_tuple = parse_ks_tuple(lcontext->context, in, &n_ks_tuple);
 520  
 521      ret = _kadm5_acl_check_permission(lcontext, KADM5_PRIV_CPW, principal);
 522      if(ret)
 523  	goto fail;
 524  
 525      ret = kadm5_randkey_principal_3(context, principal, keepold, n_ks_tuple, ks_tuple,
 526  				    &new_keys, &n_keys);
 527  
 528   fail:
 529      krb5_warn(lcontext->context, ret, "rand key principal v3: %s",
 530  	      princ ? princ : "<unknown>");
 531  
 532      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 533      CHECK(krb5_store_uint32(out, ret));
 534      if (ret == 0) {
 535  	int i;
 536  	CHECK(krb5_store_int32(out, n_keys));
 537  
 538  	for(i = 0; i < n_keys; i++){
 539  	    CHECK(krb5_store_uint32(out, new_keys[i].keytype));
 540  	    CHECK(_kadm5_xdr_store_data_xdr(out, new_keys[i].keyvalue));
 541  	    krb5_free_keyblock_contents(lcontext->context, &new_keys[i]);
 542  	}
 543  	free(new_keys);
 544      }
 545      if (ks_tuple)
 546  	free(ks_tuple);
 547      krb5_free_principal(lcontext->context, principal);
 548      if (princ)
 549  	free(princ);
 550  }
 551  
 552  
 553  static void
 554  proc_init(kadm5_server_context *lcontext,
 555  	  krb5_storage *in,
 556  	  krb5_storage *out)
 557  {
 558      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 559      CHECK(krb5_store_uint32(out, 0)); /* code */
 560      CHECK(krb5_store_uint32(out, 0)); /* code */
 561  }
 562  
 563  static void
 564  proc_get_policy(kadm5_server_context *lcontext,
 565  		krb5_storage *in,
 566  		krb5_storage *out)
 567  {
 568      CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
 569      CHECK(krb5_store_uint32(out, KADM5_AUTH_GET)); /* code */
 570  }
 571  
 572  
 573  struct proc {
 574      char *name;
 575      void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
 576  } procs[] = {
 577      { "NULL", NULL },
 578      { "create principal", proc_create_principal },
 579      { "delete principal", proc_delete_principal },
 580      { "modify principal", proc_modify_principal },
 581      { "rename principal", NULL },
 582      { "get principal", proc_get_principal },
 583      { "chpass principal", NULL },
 584      { "chrand principal v2", proc_chrand_principal_v2 },
 585      { "create policy", NULL },
 586      { "delete policy", NULL },
 587      { "modify policy", NULL },
 588      { "get policy", proc_get_policy },
 589      { "get privs", NULL },
 590      { "init", proc_init },
 591      { "get principals", NULL },
 592      { "get polices", NULL },
 593      { "setkey principal", NULL },
 594      { "setkey principal v4", NULL },
 595      { "create principal v3", proc_create_principal3 },
 596      { "chpass principal v3", NULL },
 597      { "chrand principal v3", proc_chrand_principal_v3 },
 598      { "setkey principal v3", NULL }
 599  };
 600  
 601  static krb5_error_code
 602  copyheader(krb5_storage *sp, krb5_data *data)
 603  {
 604      off_t off;
 605      ssize_t sret;
 606  
 607      off = krb5_storage_seek(sp, 0, SEEK_CUR);
 608  
 609      CHECK(krb5_data_alloc(data, off));
 610      INSIST(off >= 0 && (size_t)off == data->length);
 611      krb5_storage_seek(sp, 0, SEEK_SET);
 612      sret = krb5_storage_read(sp, data->data, data->length);
 613      INSIST(sret == off);
 614      INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
 615  
 616      return 0;
 617  }
 618  
 619  struct gctx {
 620      krb5_data handle;
 621      gss_ctx_id_t ctx;
 622      uint32_t seq_num;
 623      uint32_t protocol;
 624      int done;
 625      int inprogress;
 626      void *server_handle;
 627      void (*verify_header)(struct gctx *, struct _kadm5_xdr_call_header *, krb5_data *);
 628      void (*handle_protocol)(struct gctx *, struct _kadm5_xdr_call_header *,
 629  			    krb5_storage *, krb5_storage *);
 630      void (*reply)(struct gctx *, krb5_storage *, krb5_storage *);
 631  
 632  };
 633  
 634  static void
 635  setup_context(struct gctx *gctx, gss_name_t src_name)
 636  {
 637      kadm5_config_params realm_params;
 638      gss_buffer_desc buf;
 639      OM_uint32 maj_stat, min_stat, junk;
 640      krb5_error_code ret;
 641      char *client;
 642  	    
 643      INSIST(gctx->done);
 644  
 645      memset(&realm_params, 0, sizeof(realm_params));
 646  	    
 647      maj_stat = gss_export_name(&min_stat, src_name, &buf);
 648      EXPECT(maj_stat, GSS_S_COMPLETE);
 649      EXPECT(min_stat, 0);
 650  	    
 651      CHECK(parse_name(buf.value, buf.length, 
 652  		     GSS_KRB5_MECHANISM, &client));
 653      
 654      gss_release_buffer(&junk, &buf);
 655      
 656      krb5_warnx(context, "%s connected", client);
 657      
 658      ret = kadm5_s_init_with_password_ctx(context,
 659  					 client,
 660  					 NULL,
 661  					 KADM5_ADMIN_SERVICE,
 662  					 &realm_params,
 663  					 0, 0,
 664  					 &gctx->server_handle);
 665      EXPECT(ret, 0);
 666  }
 667  
 668  /*
 669   * GSS flavor
 670   */
 671  
 672  static void
 673  xpcgss_verify_header(struct gctx *gctx, struct _kadm5_xdr_call_header *chdr, krb5_data *header)
 674  {
 675      OM_uint32 maj_stat, min_stat;
 676      gss_buffer_desc gin, gout;
 677  
 678      EXPECT(chdr->verf.flavor, gctx->protocol);
 679  
 680      /* from first byte to last of credential */
 681      gin.value = header->data;
 682      gin.length = header->length;
 683      gout.value = chdr->verf.data.data;
 684      gout.length = chdr->verf.data.length;
 685      
 686      maj_stat = gss_verify_mic(&min_stat, gctx->ctx, &gin, &gout, NULL);
 687      EXPECT(maj_stat, GSS_S_COMPLETE);
 688  }
 689  
 690  static void
 691  rpcgss_handle_protocol(struct gctx *gctx,
 692  		       struct _kadm5_xdr_call_header *chdr,
 693  		       krb5_storage *msg,
 694  		       krb5_storage *dreply)
 695  {
 696      OM_uint32 maj_stat, min_stat, junk;
 697      gss_buffer_desc gin, gout;
 698      struct _kadm5_xdr_gcred gcred;
 699  
 700      memset(&gcred, 0, sizeof(gcred));
 701  
 702      CHECK(_kadm5_xdr_ret_gcred(&chdr->cred.data, &gcred));
 703      EXPECT(gcred.version, FLAVOR_GSS_VERSION);
 704  
 705      switch(gcred.proc) {
 706      case RPG_DATA: {
 707  	krb5_data data;
 708  	int conf_state;
 709  	uint32_t seq;
 710  	krb5_storage *sp;
 711  	
 712  	EXPECT(gcred.service, rpg_privacy);
 713  	
 714  	INSIST(gctx->done);
 715  	
 716  	INSIST(krb5_data_cmp(&gcred.handle, &gctx->handle) == 0);
 717  	
 718  	CHECK(_kadm5_xdr_ret_data_xdr(msg, &data));
 719  	
 720  	gin.value = data.data;
 721  	gin.length = data.length;
 722  	
 723  	maj_stat = gss_unwrap(&min_stat, gctx->ctx, &gin, &gout,
 724  			      &conf_state, NULL);
 725  	krb5_data_free(&data);
 726  	INSIST(maj_stat == GSS_S_COMPLETE);
 727  	INSIST(conf_state != 0);
 728  	
 729  	sp = krb5_storage_from_mem(gout.value, gout.length);
 730  	INSIST(sp != NULL);
 731  	
 732  	CHECK(krb5_ret_uint32(sp, &seq));
 733  	EXPECT(seq, gcred.seq_num);
 734  	
 735  	/*
 736  	 * Check sequence number
 737  	 */
 738  	INSIST(seq > gctx->seq_num);
 739  	gctx->seq_num = seq;
 740  	
 741  	/* 
 742  	 * If context is setup, priv data have the seq_num stored
 743  	 * first in the block, so add it here before users data is
 744  	 * added.
 745  	 */
 746  	CHECK(krb5_store_uint32(dreply, gctx->seq_num));
 747  	
 748  	if (chdr->proc >= sizeof(procs)/sizeof(procs[0])) {
 749  	    krb5_warnx(context, "proc number out of array");
 750  	} else if (procs[chdr->proc].func == NULL) {
 751  	    krb5_warnx(context, "proc '%s' never implemented", 
 752  		       procs[chdr->proc].name);
 753  	} else {
 754  	    krb5_warnx(context, "proc %s", procs[chdr->proc].name);
 755  	    INSIST(gctx->server_handle != NULL);
 756  	    (*procs[chdr->proc].func)(gctx->server_handle, sp, dreply);
 757  	}
 758  	krb5_storage_free(sp);
 759  	gss_release_buffer(&min_stat, &gout);
 760  	
 761  	break;
 762      }
 763      case RPG_INIT:
 764  	INSIST(gctx->inprogress == 0);
 765  	INSIST(gctx->ctx == NULL);
 766  	
 767  	gctx->inprogress = 1;
 768  	/* FALL THOUGH */
 769      case RPG_CONTINUE_INIT: {
 770  	gss_name_t src_name = GSS_C_NO_NAME;
 771  	krb5_data in;
 772  	
 773  	INSIST(gctx->inprogress);
 774  	
 775  	CHECK(_kadm5_xdr_ret_data_xdr(msg, &in));
 776  	
 777  	gin.value = in.data;
 778  	gin.length = in.length;
 779  	gout.value = NULL;
 780  	gout.length = 0;
 781  	
 782  	maj_stat = gss_accept_sec_context(&min_stat,
 783  					  &gctx->ctx, 
 784  					  GSS_C_NO_CREDENTIAL,
 785  					  &gin,
 786  					  GSS_C_NO_CHANNEL_BINDINGS,
 787  					  &src_name,
 788  					  NULL,
 789  					  &gout,
 790  					  NULL,
 791  					  NULL,
 792  					  NULL);
 793  	if (GSS_ERROR(maj_stat)) {
 794  	    gss_print_errors(context, maj_stat, min_stat);
 795  	    krb5_errx(context, 1, "gss error, exit");
 796  	}
 797  	if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
 798  	    
 799  	    gctx->done = 1;
 800  	    gctx->verify_header = xpcgss_verify_header;
 801  	    
 802  	    setup_context(gctx, src_name);
 803  	}
 804  	
 805  	INSIST(gctx->ctx != GSS_C_NO_CONTEXT);
 806  	
 807  	CHECK(_kadm5_xdr_store_gss_init_res(dreply, gctx->handle,
 808  					    maj_stat, min_stat, SEQ_WINDOW_SIZE, &gout));
 809  	if (gout.value)
 810  	    gss_release_buffer(&junk, &gout);
 811  	if (src_name)
 812  	    gss_release_name(&junk, &src_name);
 813  	
 814  	break;
 815      }
 816      case RPG_DESTROY:
 817  	krb5_errx(context, 1, "client destroyed gss context");
 818      default:
 819  	krb5_errx(context, 1, "client sent unknown gsscode %d", 
 820  		  (int)gcred.proc);
 821      }
 822      
 823      krb5_data_free(&gcred.handle);
 824  }
 825  
 826  static void
 827  rpcgss_reply(struct gctx *gctx, krb5_storage *dreply, krb5_storage *reply)
 828  {
 829      uint32_t seqnum = htonl(gctx->seq_num);
 830      gss_buffer_desc gin, gout;
 831      OM_uint32 maj_stat, min_stat, junk;
 832      krb5_data data;
 833  
 834      /*
 835       * The first checksum is really the checksum of the
 836       * seq_window in the rpc_gss_init_res packet, lets agree
 837       * with that.
 838       */
 839      if (gctx->seq_num == 0)
 840  	seqnum = htonl(SEQ_WINDOW_SIZE);
 841  
 842      gin.value = &seqnum;
 843      gin.length = sizeof(seqnum);
 844  
 845      maj_stat = gss_get_mic(&min_stat, gctx->ctx, 0, &gin, &gout);
 846      INSIST(maj_stat == GSS_S_COMPLETE);
 847  
 848      data.data = gout.value;
 849      data.length = gout.length;
 850  
 851      CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
 852      CHECK(_kadm5_xdr_store_data_xdr(reply, data));
 853      gss_release_buffer(&junk, &gout);
 854  
 855      CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
 856  
 857      CHECK(krb5_storage_to_data(dreply, &data));
 858  
 859      if (gctx->inprogress) {
 860  	ssize_t sret;
 861  	gctx->inprogress = 0;
 862  	sret = krb5_storage_write(reply, data.data, data.length);
 863  	INSIST(sret >= 0 && (size_t)sret == data.length);
 864  	krb5_data_free(&data);
 865      } else {
 866  	int conf_state;
 867  
 868  	gin.value = data.data;
 869  	gin.length = data.length;
 870  		
 871  	maj_stat = gss_wrap(&min_stat, gctx->ctx, 1, 0,
 872  			    &gin, &conf_state, &gout);
 873  	INSIST(maj_stat == GSS_S_COMPLETE);
 874  	INSIST(conf_state != 0);
 875  	krb5_data_free(&data);
 876  	
 877  	data.data = gout.value;
 878  	data.length = gout.length;
 879  	
 880  	_kadm5_xdr_store_data_xdr(reply, data);
 881  	gss_release_buffer(&min_stat, &gout);
 882      }
 883  }
 884  
 885  
 886  /*
 887   * GSSAPI flavor
 888   */
 889  
 890  enum {
 891      RPGA_INIT = 1,
 892      RPGA_CONTINUE_INIT = 2,
 893      RPGA_MSG = 3,
 894      RPGA_DESTORY = 4
 895  };
 896  
 897  static void
 898  xpcgssapi_verify_header(struct gctx *gctx, struct _kadm5_xdr_call_header *chdr, krb5_data *header)
 899  {
 900  #if 0
 901      OM_uint32 maj_stat, min_stat;
 902      gss_buffer_desc gin, gout;
 903  
 904      EXPECT(chdr->verf.flavor, gctx->protocol);
 905  
 906      /* from first byte to last of credential */
 907      gin.value = header->data;
 908      gin.length = header->length;
 909      gout.value = chdr->verf.data.data;
 910      gout.length = chdr->verf.data.length;
 911      
 912      maj_stat = gss_verify_mic(&min_stat, gctx->ctx, &gin, &gout, NULL);
 913      EXPECT(maj_stat, GSS_S_COMPLETE);
 914  #endif
 915  }
 916  
 917  static void
 918  rpcgssapi_handle_protocol(struct gctx *gctx,
 919  			  struct _kadm5_xdr_call_header *chdr,
 920  			  krb5_storage *msg,
 921  			  krb5_storage *dreply)
 922  {
 923      struct _kadm5_xdr_gacred gacred;
 924      OM_uint32 maj_stat, min_stat, junk;
 925      gss_buffer_desc gin, gout, gseq;
 926  
 927      CHECK(_kadm5_xdr_ret_gacred(&chdr->cred.data, &gacred));
 928      gseq.length = 0;
 929      gseq.value = NULL;
 930  
 931      if (gctx->done == 0 && chdr->proc == RPGA_INIT) {
 932  	INSIST(gacred.handle.length == 0);
 933  	INSIST(gctx->handle.length == 0);
 934  
 935  	CHECK(krb5_data_alloc(&gctx->handle, 16));
 936  	krb5_generate_random_block(gctx->handle.data, gctx->handle.length);
 937  
 938      } else {
 939  	INSIST(gacred.handle.length != 0);
 940  	INSIST(krb5_data_cmp(&gacred.handle, &gctx->handle) == 0);
 941      }
 942  
 943  
 944      if (gctx->done == 0) {
 945  	uint32_t version;
 946  	krb5_data token, out;
 947  
 948  	CHECK(krb5_ret_uint32(msg, &version));
 949  	CHECK(_kadm5_xdr_ret_data_xdr(msg, &token));
 950  
 951  	switch (chdr->proc) {
 952  	case RPGA_INIT:
 953  	    INSIST(gctx->inprogress == 0);
 954  	    INSIST(gctx->ctx == NULL);
 955  	
 956  	    INSIST(version == 3 || version == 4);
 957  
 958  	    gctx->inprogress = 1;
 959  
 960  	    /* FALL THOUGH */
 961  	case RPGA_CONTINUE_INIT: {
 962  	    gss_name_t src_name = GSS_C_NO_NAME;
 963  	
 964  	    INSIST(gctx->inprogress);
 965  	
 966  	    gin.value = token.data;
 967  	    gin.length = token.length;
 968  	    gout.value = NULL;
 969  	    gout.length = 0;
 970  	    
 971  	    maj_stat = gss_accept_sec_context(&min_stat,
 972  					      &gctx->ctx, 
 973  					      GSS_C_NO_CREDENTIAL,
 974  					      &gin,
 975  					      GSS_C_NO_CHANNEL_BINDINGS,
 976  					      &src_name,
 977  					      NULL,
 978  					      &gout,
 979  					      NULL,
 980  					      NULL,
 981  					      NULL);
 982  	    if (GSS_ERROR(maj_stat)) {
 983  		gss_print_errors(context, maj_stat, min_stat);
 984  		krb5_errx(context, 1, "gss error, exit");
 985  	    }
 986  	    if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
 987  		uint32_t netseqnum;
 988  
 989  		gctx->done = 1;
 990  		gctx->verify_header = xpcgssapi_verify_header;
 991  
 992  		setup_context(gctx, src_name);
 993  
 994  		krb5_generate_random_block(&gctx->seq_num, sizeof(gctx->seq_num));
 995  
 996  		netseqnum = htonl(gctx->seq_num);
 997  		gin.value = &netseqnum;
 998  		gin.length = sizeof(netseqnum);
 999  
1000  		CHECK(gss_wrap(&junk, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gseq));
1001  	    }
1002  
1003  	    INSIST(gctx->ctx != GSS_C_NO_CONTEXT);
1004  
1005  	    /* reply argument */
1006  
1007  	    CHECK(krb5_store_uint32(dreply, version));
1008  	    CHECK(_kadm5_xdr_store_data_xdr(dreply, gctx->handle));
1009  	    CHECK(krb5_store_uint32(dreply, maj_stat));
1010  	    CHECK(krb5_store_uint32(dreply, min_stat));
1011  
1012  	    out.data = gout.value;
1013  	    out.length = gout.length;
1014  	    CHECK(_kadm5_xdr_store_data_xdr(dreply, out));
1015  
1016  	    out.data = gseq.value;
1017  	    out.length = gseq.length;
1018  	    CHECK(_kadm5_xdr_store_data_xdr(dreply, out));
1019  
1020  	    if (gout.value)
1021  		gss_release_buffer(&junk, &gout);
1022  	    if (gseq.value)
1023  		gss_release_buffer(&junk, &gseq);
1024  	    if (src_name)
1025  		gss_release_name(&junk, &src_name);
1026  
1027  	    break;
1028  	}
1029  	default:
1030  	    krb5_errx(context, 1, "unsupported init message %d", (int)chdr->proc);
1031  	}
1032  	krb5_data_free(&token);
1033  
1034      } else if (gacred.auth_msg) {
1035  	if (chdr->proc == RPGA_MSG)
1036  	    krb5_warnx(context, "auth message MSG not supported");
1037  	else if (chdr->proc == RPGA_DESTORY)
1038  	    krb5_warnx(context, "auth message DESTROY not supported");
1039  	else
1040  	    krb5_errx(context, 1, "auth message not supported: %d", (int)chdr->proc);
1041      } else {
1042  	krb5_storage *sp;
1043  	krb5_data data;
1044  	int conf_state = 0;
1045  	uint32_t seq;
1046  
1047  	INSIST(gctx->done);
1048  	
1049  	CHECK(_kadm5_xdr_ret_data_xdr(msg, &data));
1050  	
1051  	gin.value = data.data;
1052  	gin.length = data.length;
1053  	
1054  	maj_stat = gss_unwrap(&min_stat, gctx->ctx, &gin, &gout,
1055  			      &conf_state, NULL);
1056  	krb5_data_free(&data);
1057  	INSIST(maj_stat == GSS_S_COMPLETE);
1058  	INSIST(conf_state != 0);
1059  	
1060  	sp = krb5_storage_from_mem(gout.value, gout.length);
1061  	INSIST(sp != NULL);
1062  	
1063  	CHECK(krb5_ret_uint32(sp, &seq));
1064  	
1065  	/*
1066  	 * Check sequence number
1067  	 */
1068  	INSIST(seq == gctx->seq_num + 1);
1069  	gctx->seq_num = seq + 1;
1070  	
1071  	/* 
1072  	 * If context is setup, priv data have the seq_num stored
1073  	 * first in the block, so add it here before users data is
1074  	 * added.
1075  	 */
1076  	CHECK(krb5_store_uint32(dreply, gctx->seq_num));
1077  	
1078  	if (chdr->proc >= sizeof(procs)/sizeof(procs[0])) {
1079  	    krb5_warnx(context, "proc number out of array");
1080  	} else if (procs[chdr->proc].func == NULL) {
1081  	    krb5_warnx(context, "proc '%s' never implemented", 
1082  		       procs[chdr->proc].name);
1083  	} else {
1084  	    krb5_warnx(context, "proc %s", procs[chdr->proc].name);
1085  	    INSIST(gctx->server_handle != NULL);
1086  	    (*procs[chdr->proc].func)(gctx->server_handle, sp, dreply);
1087  	}
1088  	krb5_storage_free(sp);
1089  	gss_release_buffer(&min_stat, &gout);
1090      }
1091  
1092      krb5_data_free(&gacred.handle);
1093  }
1094  
1095  static void
1096  rpcgssapi_reply(struct gctx *gctx, krb5_storage *dreply, krb5_storage *reply)
1097  {
1098      uint32_t seqnum = htonl(gctx->seq_num);
1099      gss_buffer_desc gin, gout;
1100      OM_uint32 maj_stat, min_stat, junk;
1101      krb5_data data;
1102  
1103      gin.value = &seqnum;
1104      gin.length = sizeof(seqnum);
1105  
1106      maj_stat = gss_wrap(&min_stat, gctx->ctx, 0, GSS_C_QOP_DEFAULT, &gin, NULL, &gout);
1107      INSIST(maj_stat == GSS_S_COMPLETE);
1108  
1109      data.data = gout.value;
1110      data.length = gout.length;
1111  
1112      CHECK(krb5_store_uint32(reply, FLAVOR_GSS_OLD));
1113      CHECK(_kadm5_xdr_store_data_xdr(reply, data));
1114      gss_release_buffer(&junk, &gout);
1115  
1116      CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1117  
1118      CHECK(krb5_storage_to_data(dreply, &data));
1119  
1120      if (gctx->inprogress) {
1121  	ssize_t sret;
1122  	gctx->inprogress = 0;
1123  	sret = krb5_storage_write(reply, data.data, data.length);
1124  	INSIST(sret >= 0 && (size_t)sret == data.length);
1125  	krb5_data_free(&data);
1126      } else {
1127  	int conf_state;
1128  
1129  	gin.value = data.data;
1130  	gin.length = data.length;
1131  		
1132  	maj_stat = gss_wrap(&min_stat, gctx->ctx, 1, 0,
1133  			    &gin, &conf_state, &gout);
1134  	INSIST(maj_stat == GSS_S_COMPLETE);
1135  	INSIST(conf_state != 0);
1136  	krb5_data_free(&data);
1137  	
1138  	data.data = gout.value;
1139  	data.length = gout.length;
1140  	
1141  	_kadm5_xdr_store_data_xdr(reply, data);
1142  	gss_release_buffer(&min_stat, &gout);
1143      }
1144  }
1145  
1146  
1147  /*
1148   *
1149   */
1150  
1151  
1152  static int
1153  process_stream(krb5_context lcontext, 
1154  	       unsigned char *buf, size_t ilen,
1155  	       krb5_storage *sp)
1156  {
1157      krb5_error_code ret;
1158      krb5_storage *msg, *reply, *dreply;
1159      struct gctx gctx;
1160  
1161      memset(&gctx, 0, sizeof(gctx));
1162  
1163      msg = krb5_storage_emem();
1164      reply = krb5_storage_emem();
1165      dreply = krb5_storage_emem();
1166  
1167      /*
1168       * First packet comes partly from the caller
1169       */
1170  
1171      INSIST(ilen >= 4);
1172  
1173      while (1) {
1174  	struct _kadm5_xdr_call_header chdr;
1175  	uint32_t mtype;
1176  	krb5_data headercopy;
1177  
1178  	krb5_storage_truncate(dreply, 0);
1179  	krb5_storage_truncate(reply, 0);
1180  	krb5_storage_truncate(msg, 0);
1181  
1182  	krb5_data_zero(&headercopy);
1183  	memset(&chdr, 0, sizeof(chdr));
1184  
1185  	/*
1186  	 * This is very icky to handle the the auto-detection between
1187  	 * the Heimdal protocol and the MIT ONC-RPC based protocol.
1188  	 */
1189  
1190  	if (ilen) {
1191  	    int last_fragment;
1192  	    unsigned long len;
1193  	    ssize_t slen;
1194  	    unsigned char tmp[4];
1195  
1196  	    if (ilen < 4) {
1197  		memcpy(tmp, buf, ilen);
1198  		slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
1199  		INSIST(slen >= 0 && (size_t)slen == sizeof(tmp) - ilen);
1200  
1201  		ilen = sizeof(tmp);
1202  		buf = tmp;
1203  	    }
1204  	    INSIST(ilen >= 4);
1205  	    
1206  	    _krb5_get_int(buf, &len, 4);
1207  	    last_fragment = (len & LAST_FRAGMENT) != 0;
1208  	    len &= ~LAST_FRAGMENT;
1209  	    
1210  	    ilen -= 4;
1211  	    buf += 4;
1212  
1213  	    if (ilen) {
1214  		if (len < ilen) {
1215  		    slen = krb5_storage_write(msg, buf, len);
1216  		    INSIST(slen >= 0 && (size_t)slen == len);
1217  		    ilen -= len;
1218  		    len = 0;
1219  		} else {
1220  		    slen = krb5_storage_write(msg, buf, ilen);
1221  		    INSIST(slen >= 0 && (size_t)slen == ilen);
1222  		    len -= ilen;
1223  		}
1224  	    }
1225  
1226  	    CHECK(read_data(sp, msg, len));
1227  	    
1228  	    if (!last_fragment) {
1229  		ret = collect_fragments(sp, msg);
1230  		if (ret == HEIM_ERR_EOF)
1231  		    krb5_errx(lcontext, 0, "client disconnected");
1232  		INSIST(ret == 0);
1233  	    }
1234  	} else {
1235  
1236  	    ret = collect_fragments(sp, msg);
1237  	    if (ret == HEIM_ERR_EOF)
1238  		krb5_errx(lcontext, 0, "client disconnected");
1239  	    INSIST(ret == 0);
1240  	}
1241  	krb5_storage_seek(msg, 0, SEEK_SET);
1242  
1243  	CHECK(krb5_ret_uint32(msg, &chdr.xid));
1244  	CHECK(krb5_ret_uint32(msg, &mtype));
1245  	CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
1246  	CHECK(krb5_ret_uint32(msg, &chdr.prog));
1247  	CHECK(krb5_ret_uint32(msg, &chdr.vers));
1248  	CHECK(krb5_ret_uint32(msg, &chdr.proc));
1249  	CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.cred));
1250  	CHECK(copyheader(msg, &headercopy));
1251  	CHECK(_kadm5_xdr_ret_auth_opaque(msg, &chdr.verf));
1252  
1253  	EXPECT(chdr.rpcvers, RPC_VERSION);
1254  	EXPECT(chdr.prog, KADM_SERVER);
1255  	EXPECT(chdr.vers, VVERSION);
1256  	if (gctx.protocol == 0) {
1257  	    gctx.protocol = chdr.cred.flavor;
1258  
1259  	    INSIST(gctx.handle_protocol == NULL);
1260  
1261  	    switch(gctx.protocol) {
1262  	    case FLAVOR_GSS:
1263  		gctx.handle_protocol = rpcgss_handle_protocol;
1264  		gctx.reply = rpcgss_reply;
1265  		break;
1266  	    case FLAVOR_GSS_OLD:
1267  		gctx.handle_protocol = rpcgssapi_handle_protocol;
1268  		gctx.reply = rpcgssapi_reply;
1269  		break;
1270  	    default:
1271  		krb5_errx(lcontext, 0, "unsupported protocol version: %d", (int)gctx.protocol);
1272  	    }
1273  
1274  	} else {
1275  	    EXPECT(chdr.cred.flavor, gctx.protocol);
1276  	}
1277  
1278  	if (gctx.verify_header)
1279  	    gctx.verify_header(&gctx, &chdr, &headercopy);
1280  
1281  	gctx.handle_protocol(&gctx, &chdr, msg, dreply);
1282  
1283  	krb5_data_free(&chdr.cred.data);
1284  	krb5_data_free(&chdr.verf.data);
1285  	krb5_data_free(&headercopy);
1286  
1287  	CHECK(krb5_store_uint32(reply, chdr.xid));
1288  	CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
1289  	CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
1290  
1291  	if (!gctx.done) {
1292  	    krb5_data data;
1293  	    ssize_t sret;
1294  
1295  	    CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
1296  	    CHECK(krb5_store_uint32(reply, 0)); /* length */
1297  
1298  	    CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
1299  
1300  	    CHECK(krb5_storage_to_data(dreply, &data));
1301  	    sret = krb5_storage_write(reply, data.data, data.length);
1302  	    INSIST(sret >= 0 && (size_t)sret == data.length);
1303  	    krb5_data_free(&data);
1304  	} else {
1305  	    INSIST(gctx.reply != NULL);
1306  
1307  	    gctx.reply(&gctx, dreply, reply);
1308  	}
1309  
1310  	{
1311  	    krb5_data data;
1312  	    ssize_t sret;
1313  	    CHECK(krb5_storage_to_data(reply, &data));
1314  	    CHECK(krb5_store_uint32(sp, ((uint32_t)data.length) | LAST_FRAGMENT));
1315  	    sret = krb5_storage_write(sp, data.data, data.length);
1316  	    INSIST(sret >= 0 && (size_t)sret == data.length);
1317  	    krb5_data_free(&data);
1318  	}
1319  
1320      }
1321  }
1322  
1323  int
1324  handle_mit(krb5_context lcontext, void *buf, size_t len, krb5_socket_t sock)
1325  {
1326      krb5_storage *sp;
1327  
1328      dcontext = context;
1329  
1330      sp = krb5_storage_from_fd(sock);
1331      INSIST(sp != NULL);
1332      
1333      process_stream(lcontext, buf, len, sp);
1334  
1335      return 0;
1336  }