/ lib / kadm5 / mit.c
mit.c
   1  /*
   2   * Copyright (c) 2008 Kungliga Tekniska Högskolan
   3   * (Royal Institute of Technology, Stockholm, Sweden).
   4   * All rights reserved.
   5   *
   6   * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
   7   *
   8   * Redistribution and use in source and binary forms, with or without
   9   * modification, are permitted provided that the following conditions
  10   * are met:
  11   *
  12   * 1. Redistributions of source code must retain the above copyright
  13   *    notice, this list of conditions and the following disclaimer.
  14   *
  15   * 2. Redistributions in binary form must reproduce the above copyright
  16   *    notice, this list of conditions and the following disclaimer in the
  17   *    documentation and/or other materials provided with the distribution.
  18   *
  19   * 3. Neither the name of the Institute nor the names of its contributors
  20   *    may be used to endorse or promote products derived from this software
  21   *    without specific prior written permission.
  22   *
  23   * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  24   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26   * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  27   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  29   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  30   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  32   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  33   * SUCH DAMAGE.
  34   */
  35  
  36  #include "kadm5_locl.h"
  37  
  38  #include <syslog.h>
  39  #include <gssapi_krb5.h>
  40  
  41  #define CHECK(x)							\
  42  	do {								\
  43  		int __r;						\
  44  		if ((__r = (x))) {					\
  45  			syslog(LOG_ERR, "kadmin: protocol error:%d", __LINE__);			\
  46  			_exit(1);					\
  47  		}							\
  48  	} while(0)
  49  
  50  #define INSIST(x) CHECK(!(x))
  51  
  52  #define VERSION2 0x12345702
  53  
  54  #define LAST_FRAGMENT 0x80000000
  55  
  56  #define RPC_VERSION 2
  57  #define KADM_SERVER 2112
  58  #define VVERSION 2
  59  #define FLAVOR_GSS 6
  60  #define FLAVOR_GSS_VERSION 1
  61  
  62  #define PROC_NULL 0
  63  #define PROC_CREATE_PRINCIPAL 1
  64  #define PROC_DELETE_PRINCIPAL 2
  65  #define PROC_MODIFY_PRINCIPAL 3
  66  #define PROC_GET_PRINCIPAL 5
  67  #define PROC_CHRAND_PRINCIPAL 7
  68  #define PROC_CREATE_PRINCIPAL3 18
  69  
  70  
  71  struct call_header {
  72      uint32_t xid;
  73      uint32_t rpcvers;
  74      uint32_t prog;
  75      uint32_t vers;
  76      uint32_t proc;
  77      struct _kadm5_xdr_opaque_auth cred;
  78      struct _kadm5_xdr_opaque_auth verf;
  79  };
  80  
  81  enum {
  82      RPG_DATA = 0,
  83      RPG_INIT = 1,
  84      RPG_CONTINUE_INIT = 2,
  85      RPG_DESTROY = 3
  86  };
  87  
  88  enum {
  89      rpg_privacy = 3
  90  };
  91  
  92  #if 0
  93  static void
  94  gss_error(krb5_context context,
  95  	  gss_OID mech, OM_uint32 type, OM_uint32 error)
  96  {
  97      OM_uint32 new_stat;
  98      OM_uint32 msg_ctx = 0;
  99      gss_buffer_desc status_string;
 100      OM_uint32 ret;
 101  
 102      do {
 103  	ret = gss_display_status (&new_stat,
 104  				  error,
 105  				  type,
 106  				  mech,
 107  				  &msg_ctx,
 108  				  &status_string);
 109  	krb5_warnx(context, "%.*s",
 110  		   (int)status_string.length,
 111  		   (char *)status_string.value);
 112  	gss_release_buffer (&new_stat, &status_string);
 113      } while (!GSS_ERROR(ret) && msg_ctx != 0);
 114  }
 115  
 116  static void
 117  gss_print_errors (krb5_context context,
 118  		  OM_uint32 maj_stat, OM_uint32 min_stat)
 119  {
 120      gss_error(context, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
 121      gss_error(context, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
 122  }
 123  #endif
 124  
 125  static int
 126  read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
 127  {
 128      char buf[1024];
 129  
 130      while (len) {
 131  	size_t tlen = len;
 132  	ssize_t slen;
 133  
 134  	if (tlen > sizeof(buf))
 135  	    tlen = sizeof(buf);
 136  
 137  	slen = krb5_storage_read(sp, buf, tlen);
 138  	INSIST(slen >= 0 && (size_t)slen == tlen);
 139  
 140  	slen = krb5_storage_write(msg, buf, tlen);
 141  	INSIST(slen >= 0 || (size_t)slen == tlen);
 142  
 143  	len -= tlen;
 144      }
 145      return 0;
 146  }
 147  
 148  static int
 149  collect_fragments(krb5_storage *sp, krb5_storage *msg)
 150  {
 151      krb5_error_code ret;
 152      uint32_t len;
 153      int last_fragment;
 154      size_t total_len = 0;
 155  
 156      do {
 157  	ret = krb5_ret_uint32(sp, &len);
 158  	if (ret)
 159  	    return ret;
 160  
 161  	last_fragment = (len & LAST_FRAGMENT);
 162  	len &= ~LAST_FRAGMENT;
 163  
 164  	CHECK(read_data(sp, msg, len));
 165  	total_len += len;
 166  
 167      } while(!last_fragment || total_len == 0);
 168  
 169      return 0;
 170  }
 171  
 172  struct grpc_client {
 173      krb5_data handle;
 174      krb5_data last_cred;
 175      gss_ctx_id_t ctx;
 176      krb5_storage *sp;
 177      uint32_t seq_num;
 178      uint32_t xid;
 179      int done;
 180      int inprogress;
 181  };
 182  
 183  static krb5_error_code
 184  xdr_close_connection(krb5_context context,
 185  		     struct grpc_client *client)
 186  {
 187      free(client);
 188      return 0;
 189  }
 190  
 191  static krb5_error_code
 192  xdr_send_request(krb5_context context,
 193  		 struct grpc_client *client,
 194  		 uint32_t xid,
 195  		 uint32_t proc,
 196  		 krb5_data *out)
 197  {
 198      struct _kadm5_xdr_opaque_auth cred, verf;
 199      struct _kadm5_xdr_gcred gcred;
 200      OM_uint32 maj_stat, min_stat;
 201      gss_buffer_desc gin, gout;
 202      krb5_storage *msg;
 203      krb5_data data;
 204      size_t sret;
 205  
 206      msg = krb5_storage_emem();
 207  
 208      memset(&gcred, 0, sizeof(gcred));
 209      memset(&cred, 0, sizeof(cred));
 210      memset(&verf, 0, sizeof(verf));
 211  
 212      cred.flavor = FLAVOR_GSS;
 213      cred.data.data = NULL;
 214      cred.data.length = 0;
 215  
 216      gcred.version = FLAVOR_GSS_VERSION;
 217      if (client->done)
 218  	gcred.proc = RPG_DATA;
 219      else
 220  	gcred.proc = RPG_INIT;
 221      gcred.service = rpg_privacy;
 222      gcred.handle = client->handle;
 223      gcred.seq_num = client->seq_num;
 224  
 225      CHECK(_kadm5_xdr_store_gcred(&gcred, &cred.data));
 226  
 227      CHECK(krb5_store_uint32(msg, xid));
 228      CHECK(krb5_store_uint32(msg, 0)); /* mtype ? */
 229      CHECK(krb5_store_uint32(msg, RPC_VERSION));
 230      CHECK(krb5_store_uint32(msg, KADM_SERVER));
 231      CHECK(krb5_store_uint32(msg, VVERSION));
 232      CHECK(krb5_store_uint32(msg, proc));
 233      CHECK(_kadm5_xdr_store_auth_opaque(msg, &cred));
 234  
 235      /* create header verf */
 236      if (client->done) {
 237  	krb5_storage_to_data(msg, &data);
 238  
 239  	gin.value = data.data;
 240  	gin.length = data.length;
 241  
 242  	maj_stat = gss_get_mic(&min_stat, client->ctx, 0, &gin, &gout);
 243  	krb5_data_free(&data);
 244  	INSIST(maj_stat == GSS_S_COMPLETE);
 245  
 246  	verf.flavor = FLAVOR_GSS;
 247  	verf.data.data = gout.value;
 248  	verf.data.length = gout.length;
 249  
 250  	CHECK(_kadm5_xdr_store_auth_opaque(msg, &verf));
 251  	gss_release_buffer(&min_stat, &gout);
 252      } else {
 253  	verf.flavor = FLAVOR_GSS;
 254  	verf.data.data = NULL;
 255  	verf.data.length = 0;
 256  	CHECK(_kadm5_xdr_store_auth_opaque(msg, &verf));
 257      }
 258  
 259      if (!client->done) {
 260  	sret = krb5_storage_write(msg, out->data, out->length);
 261  	INSIST(sret == out->length);
 262      } else if (client->inprogress) {
 263  	client->inprogress = 0;
 264  	sret = krb5_storage_write(msg, out->data, out->length);
 265  	INSIST(sret == out->length);
 266      } else {
 267  	krb5_storage *reply;
 268  	int conf_state;
 269  
 270  	reply = krb5_storage_emem();
 271  
 272  	krb5_store_uint32(reply, client->seq_num);
 273  	sret = krb5_storage_write(reply, out->data, out->length);
 274  	INSIST(sret == out->length);
 275  
 276  	krb5_storage_to_data(reply, &data);
 277  	krb5_storage_free(reply);
 278  
 279  	gin.value = data.data;
 280  	gin.length = data.length;
 281  
 282  	maj_stat = gss_wrap(&min_stat, client->ctx, 1, 0,
 283  			    &gin, &conf_state, &gout);
 284  	INSIST(maj_stat == GSS_S_COMPLETE);
 285  	INSIST(conf_state != 0);
 286  	krb5_data_free(&data);
 287  
 288  	data.data = gout.value;
 289  	data.length = gout.length;
 290  
 291  	CHECK(_kadm5_xdr_store_data_xdr(msg, data));
 292  	gss_release_buffer(&min_stat, &gout);
 293      }
 294  
 295      /* write packet to output stream */
 296      {
 297  	CHECK(krb5_storage_to_data(msg, &data));
 298  	CHECK(krb5_store_uint32(client->sp, ((uint32_t)data.length) | LAST_FRAGMENT));
 299  	sret = krb5_storage_write(client->sp, data.data, data.length);
 300  	INSIST(sret == data.length);
 301  	krb5_data_free(&data);
 302      }
 303  
 304      return 0;
 305  }
 306  
 307  static krb5_error_code
 308  xdr_recv_reply(krb5_context context,
 309  	       struct grpc_client *client,
 310  	       uint32_t *xid,
 311  	       krb5_storage **out)
 312  {
 313      OM_uint32 maj_stat, min_stat;
 314      gss_buffer_desc gin, gout;
 315      krb5_error_code ret;
 316      krb5_storage *reply;
 317      krb5_data data;
 318      int conf_state;
 319      uint32_t tmp;
 320  
 321      reply = krb5_storage_emem();
 322      INSIST(reply != NULL);
 323  
 324      ret = collect_fragments(client->sp, reply);
 325      INSIST(ret == 0);
 326  
 327      krb5_storage_seek(reply, 0, SEEK_SET);
 328  
 329      CHECK(krb5_ret_uint32(reply, xid));
 330      CHECK(krb5_ret_uint32(reply, &tmp)); /* REPLY */
 331      INSIST(tmp == 1);
 332      CHECK(krb5_ret_uint32(reply, &tmp)); /* MSG_ACCEPTED */
 333      INSIST(tmp == 0);
 334  
 335  
 336      CHECK(krb5_ret_uint32(reply, &tmp)); /* flavor_gss */
 337      INSIST(tmp == FLAVOR_GSS);
 338  
 339      CHECK(_kadm5_xdr_ret_data_xdr(reply, &data));
 340  
 341      if (client->done) {
 342  	uint32_t seqnum = htonl(client->seq_num);
 343  
 344  	gin.value = &seqnum;
 345  	gin.length = sizeof(seqnum);
 346  	gout.value = data.data;
 347  	gout.length = data.length;
 348  
 349  	maj_stat = gss_verify_mic(&min_stat, client->ctx, &gin, &gout, NULL);
 350  	krb5_data_free(&data);
 351  	INSIST(maj_stat == GSS_S_COMPLETE);
 352      } else {
 353  	krb5_data_free(&client->last_cred);
 354  	client->last_cred = data;
 355      }
 356      CHECK(krb5_ret_uint32(reply, &tmp)); /* SUCCESS */
 357      INSIST(tmp == 0);
 358  
 359      if (client->done) {
 360  	/* read body */
 361  	CHECK(_kadm5_xdr_ret_data_xdr(reply, &data));
 362  
 363  	krb5_storage_free(reply);
 364  
 365  	gin.value = data.data;
 366  	gin.length = data.length;
 367  
 368  	maj_stat = gss_unwrap(&min_stat, client->ctx, &gin, &gout,
 369  			      &conf_state, NULL);
 370  	krb5_data_free(&data);
 371  	INSIST(maj_stat == GSS_S_COMPLETE);
 372  	INSIST(conf_state != 0);
 373  
 374  	/* make reply cleartext */
 375  	*out = krb5_storage_from_mem_copy(gout.value, gout.length);
 376  	INSIST(*out != NULL);
 377  
 378  	/* check seq num */
 379  	CHECK(krb5_ret_uint32(*out, &tmp));
 380  	INSIST(tmp == client->seq_num);
 381  
 382  	gss_release_buffer(&min_stat, &gout);
 383      } else {
 384  	*out = reply;
 385      }
 386  
 387      return 0;
 388  }
 389  
 390  static krb5_error_code
 391  xdr_process_request(krb5_context context,
 392  		    struct grpc_client *client,
 393  		    uint32_t proc,
 394  		    krb5_data *in,
 395  		    krb5_storage **out)
 396  {
 397      krb5_error_code ret;
 398      uint32_t xid;
 399  
 400      client->xid++;
 401      client->seq_num++;
 402  
 403      ret = xdr_send_request(context, client, client->xid, proc, in);
 404      if (ret)
 405  	return ret;
 406      ret = xdr_recv_reply(context, client, &xid, out);
 407      if (ret == 0) {
 408  	INSIST(client->xid == xid);
 409      }
 410      return ret;
 411  }
 412  
 413  static kadm5_ret_t
 414  kadm5_mit_chpass_principal(void *server_handle,
 415  			   krb5_principal principal,
 416  			   int keepold,
 417  			   const char *password,
 418  			   int n_ks_tuple,
 419  			   krb5_key_salt_tuple *ks_tuple)
 420  {
 421      kadm5_mit_context *context = server_handle;
 422      krb5_set_error_message(context->context, KADM5_RPC_ERROR,
 423  			   "Function not implemented");
 424      return KADM5_RPC_ERROR;
 425  }
 426  
 427  static kadm5_ret_t
 428  kadm5_mit_chpass_principal_with_key(void *server_handle,
 429  				    krb5_principal princ,
 430  				    int keepold,
 431  				    int n_key_data,
 432  				    krb5_key_data *key_data)
 433  {
 434      kadm5_mit_context *context = server_handle;
 435      krb5_set_error_message(context->context, KADM5_RPC_ERROR,
 436  			   "Function not implemented");
 437      return KADM5_RPC_ERROR;
 438  }
 439  
 440  static kadm5_ret_t
 441  kadm5_mit_create_principal(void *server_handle,
 442  			   kadm5_principal_ent_t entry,
 443  			   uint32_t mask,
 444  			   const char *password,
 445  			   int n_ks_tuple,
 446  			   krb5_key_salt_tuple *ks_tuple)
 447  {
 448      kadm5_mit_context *context = server_handle;
 449      krb5_storage *sp = NULL;
 450      krb5_error_code ret;
 451      uint32_t retcode;
 452      krb5_data in;
 453  
 454      /* Build request */
 455  
 456      sp = krb5_storage_emem();
 457  
 458      CHECK(krb5_store_uint32(sp, VERSION2));
 459      CHECK(_kadm5_xdr_store_principal_ent(context->context, sp, entry));
 460      CHECK(krb5_store_uint32(sp, mask));
 461      CHECK(_kadm5_xdr_store_string_xdr(sp, password));
 462  
 463      krb5_storage_to_data(sp, &in);
 464      krb5_storage_free(sp);
 465  
 466      ret = xdr_process_request(context->context, context->gsscontext,
 467  			      PROC_CREATE_PRINCIPAL, &in, &sp);
 468      krb5_data_free(&in);
 469      if (ret)
 470  	return ret;
 471  
 472      /* Read reply */
 473  
 474      CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
 475      INSIST(retcode == VERSION2);
 476      CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
 477      if (retcode == 0) {
 478  #if 0
 479  	CHECK(_kadm5_xdr_ret_principal_ent(context->context, out, ent));
 480  #endif
 481      }
 482      krb5_storage_free(sp);
 483  
 484      return (krb5_error_code)retcode;
 485  }
 486  
 487  static kadm5_ret_t
 488  kadm5_mit_delete_principal(void *server_handle, krb5_principal principal)
 489  {
 490      kadm5_mit_context *context = server_handle;
 491      krb5_storage *sp = NULL;
 492      krb5_error_code ret;
 493      uint32_t retcode;
 494      krb5_data in;
 495  
 496      /* Build request */
 497  
 498      sp = krb5_storage_emem();
 499  
 500      CHECK(krb5_store_uint32(sp, VERSION2));
 501      CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal));
 502  
 503      krb5_storage_to_data(sp, &in);
 504      krb5_storage_free(sp);
 505  
 506      ret = xdr_process_request(context->context, context->gsscontext,
 507  			      PROC_DELETE_PRINCIPAL, &in, &sp);
 508      krb5_data_free(&in);
 509      if (ret)
 510  	return ret;
 511  
 512      /* Read reply */
 513  
 514      CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
 515      INSIST(retcode == VERSION2);
 516      CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
 517      krb5_storage_free(sp);
 518  
 519      return (krb5_error_code)retcode;
 520  }
 521  
 522  static kadm5_ret_t
 523  kadm5_mit_flush(void *server_handle)
 524  {
 525      kadm5_mit_context *context = server_handle;
 526      krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
 527      return KADM5_RPC_ERROR;
 528  }
 529  
 530  static kadm5_ret_t
 531  kadm5_mit_destroy(void *server_handle)
 532  {
 533      kadm5_mit_context *context = server_handle;
 534  
 535      krb5_free_principal(context->context, context->caller);
 536      xdr_close_connection(context->context, context->gsscontext);
 537      if(context->my_context)
 538  	krb5_free_context(context->context);
 539      return 0;
 540  }
 541  
 542  static kadm5_ret_t
 543  kadm5_mit_get_principal(void *server_handle,
 544  			krb5_principal principal,
 545  			kadm5_principal_ent_t entry,
 546  			uint32_t mask)
 547  {
 548      kadm5_mit_context *context = server_handle;
 549      krb5_storage *sp = NULL;
 550      krb5_error_code ret;
 551      uint32_t retcode;
 552      krb5_data in;
 553  
 554      /* Build request */
 555  
 556      sp = krb5_storage_emem();
 557  
 558      CHECK(krb5_store_uint32(sp, VERSION2));
 559      CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal));
 560      CHECK(krb5_store_uint32(sp, mask));
 561  
 562      krb5_storage_to_data(sp, &in);
 563      krb5_storage_free(sp);
 564  
 565      ret = xdr_process_request(context->context, context->gsscontext,
 566  			      PROC_GET_PRINCIPAL, &in, &sp);
 567      krb5_data_free(&in);
 568      if (ret)
 569  	return ret;
 570  
 571      /* Read reply */
 572  
 573      CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
 574      INSIST(retcode == VERSION2);
 575      CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
 576      if (retcode == 0) {
 577  	CHECK(_kadm5_xdr_ret_principal_ent(context->context, sp, entry));
 578      }
 579      krb5_storage_free(sp);
 580  
 581      return (krb5_error_code)retcode;
 582  }
 583  
 584  static kadm5_ret_t
 585  kadm5_mit_get_principals(void *server_handle,
 586  			 const char *expression,
 587  			 char ***principals,
 588  			 int *count)
 589  {
 590      kadm5_mit_context *context = server_handle;
 591      krb5_set_error_message(context->context, KADM5_RPC_ERROR,
 592  			   "Function not implemented");
 593      return KADM5_RPC_ERROR;
 594  }
 595  
 596  static kadm5_ret_t
 597  kadm5_mit_get_privs(void *server_handle, uint32_t*privs)
 598  {
 599      kadm5_mit_context *context = server_handle;
 600      krb5_set_error_message(context->context, KADM5_RPC_ERROR,
 601  			   "Function not implemented");
 602      return KADM5_RPC_ERROR;
 603  }
 604  
 605  static kadm5_ret_t
 606  kadm5_mit_modify_principal(void *server_handle,
 607  			  kadm5_principal_ent_t entry,
 608  			  uint32_t mask)
 609  {
 610      kadm5_mit_context *context = server_handle;
 611      krb5_storage *sp = NULL;
 612      krb5_error_code ret;
 613      uint32_t retcode;
 614      krb5_data in;
 615  
 616      /* Build request */
 617  
 618      sp = krb5_storage_emem();
 619  
 620      CHECK(krb5_store_uint32(sp, VERSION2));
 621      CHECK(_kadm5_xdr_store_principal_ent(context->context, sp, entry));
 622      CHECK(krb5_store_uint32(sp, mask));
 623  
 624      krb5_storage_to_data(sp, &in);
 625      krb5_storage_free(sp);
 626  
 627      ret = xdr_process_request(context->context, context->gsscontext,
 628  			      PROC_MODIFY_PRINCIPAL, &in, &sp);
 629      krb5_data_free(&in);
 630      if (ret)
 631  	return ret;
 632  
 633      /* Read reply */
 634  
 635      CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
 636  #if 0 /* modify doesn't set api version */
 637      INSIST(retcode == VERSION2);
 638  #endif
 639      CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
 640      krb5_storage_free(sp);
 641  
 642      return (krb5_error_code)retcode;
 643  }
 644  
 645  static kadm5_ret_t
 646  kadm5_mit_rename_principal(void *server_handle,
 647  			  krb5_principal from,
 648  			  krb5_principal to)
 649  {
 650      kadm5_mit_context *context = server_handle;
 651      krb5_set_error_message(context->context, KADM5_RPC_ERROR,
 652  			   "Function not implemented");
 653      return KADM5_RPC_ERROR;
 654  }
 655  
 656  static kadm5_ret_t
 657  kadm5_mit_randkey_principal(void *server_handle,
 658  			    krb5_principal principal,
 659  			    krb5_boolean keepold,
 660  			    int n_ks_tuple,
 661  			    krb5_key_salt_tuple *ks_tuple,
 662  			    krb5_keyblock **keys,
 663  			    int *n_keys)
 664  {
 665      kadm5_mit_context *context = server_handle;
 666      krb5_storage *sp = NULL;
 667      krb5_error_code ret;
 668      uint32_t retcode;
 669      krb5_data in;
 670  
 671      /* Build request */
 672  
 673      sp = krb5_storage_emem();
 674  
 675      CHECK(krb5_store_uint32(sp, VERSION2));
 676      CHECK(_kadm5_xdr_store_principal_xdr(context->context, sp, principal));
 677  
 678      krb5_storage_to_data(sp, &in);
 679      krb5_storage_free(sp);
 680  
 681      ret = xdr_process_request(context->context, context->gsscontext,
 682  			      PROC_CHRAND_PRINCIPAL, &in, &sp);
 683      krb5_data_free(&in);
 684      if (ret)
 685  	return ret;
 686  
 687      /* Read reply */
 688  
 689      CHECK(krb5_ret_uint32(sp, &retcode)); /* api version */
 690      INSIST(retcode == VERSION2);
 691      CHECK(krb5_ret_uint32(sp, &retcode)); /* code */
 692      if (retcode == 0) {
 693  	uint32_t enctype, num;
 694  	int i;
 695  
 696  	CHECK(krb5_ret_uint32(sp, &num));
 697  	CHECK(num < 2000);
 698  
 699  	*n_keys = num;
 700  
 701  	*keys = calloc(num, sizeof((*keys)[0]));
 702  	for (i = 0; i < *n_keys; i++) {
 703  	    CHECK(krb5_ret_uint32(sp, &enctype));
 704  	    (*keys)[i].keytype = enctype;
 705  	    CHECK(_kadm5_xdr_ret_data_xdr(sp, &(*keys)[i].keyvalue));
 706  	}
 707      }
 708      krb5_storage_free(sp);
 709  
 710      return (krb5_error_code)retcode;
 711  }
 712  
 713  /*
 714   *
 715   */
 716  
 717  static void
 718  verify_seq_num(struct grpc_client *c, uint32_t seq, krb5_data *cred)
 719  {
 720      OM_uint32 maj_stat, min_stat;
 721      gss_buffer_desc gin, gout;
 722  
 723      seq = htonl(seq);
 724  
 725      gin.value = &seq;
 726      gin.length = sizeof(seq);
 727      gout.value = cred->data;
 728      gout.length = cred->length;
 729  
 730      INSIST(cred->length != 0);
 731  
 732      maj_stat = gss_verify_mic(&min_stat, c->ctx, &gin, &gout, NULL);
 733      krb5_data_free(&c->last_cred);
 734      INSIST(maj_stat == GSS_S_COMPLETE);
 735  }
 736  
 737  
 738  static krb5_error_code
 739  xdr_setup_connection(krb5_context context,
 740  		     const char *service,
 741  		     const char *hostname,
 742  		     unsigned port,
 743  		     struct grpc_client **client)
 744  {
 745      struct addrinfo *ai, *a, hints;
 746      char portstr[NI_MAXSERV];
 747      struct grpc_client *c;
 748      krb5_error_code ret;
 749      int error, s = -1;
 750      OM_uint32 maj_stat, maj_stat2, min_stat, ret_flags;
 751      gss_buffer_desc gin, gout;
 752      gss_name_t target;
 753      int try_kadmin_admin = 0;
 754      uint32_t seq_window = 0;
 755  
 756      c = calloc(1, sizeof(*c));
 757      if (c == NULL)
 758  	return ENOMEM;
 759  
 760      memset (&hints, 0, sizeof(hints));
 761      hints.ai_socktype = SOCK_STREAM;
 762      hints.ai_protocol = IPPROTO_TCP;
 763  
 764      snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
 765  
 766      error = getaddrinfo (hostname, portstr, &hints, &ai);
 767      if (error) {
 768  	free(c);
 769  	return KADM5_BAD_SERVER_NAME;
 770      }
 771  
 772      for (a = ai; a != NULL; a = a->ai_next) {
 773  	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
 774  	if (s < 0)
 775  	    continue;
 776  	socket_set_nopipe(s, 1);
 777  	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
 778  	    close (s);
 779  	    continue;
 780  	}
 781  	break;
 782      }
 783      if (a == NULL || s < 0) {
 784  	freeaddrinfo (ai);
 785  	return KADM5_FAILURE;
 786      }
 787  
 788      c->sp = krb5_storage_from_fd(s);
 789      close(s);
 790  
 791   try_again:
 792      {
 793  	char *str;
 794  	if (!try_kadmin_admin)
 795  	    asprintf(&str, "%s@%s", service, hostname);
 796  	else
 797  	    asprintf(&str, "kadmin@admin");
 798  	gin.value = str;
 799  	gin.length = strlen(str);
 800  	maj_stat = gss_import_name(&min_stat, &gin, GSS_C_NT_HOSTBASED_SERVICE, &target);
 801  	if (maj_stat != GSS_S_COMPLETE) {
 802  	    krb5_set_error_message(context, ENOENT,
 803  				   "failed to import name: %s", str);
 804  	    free(str);
 805  	    return ENOENT;
 806  	}
 807  	free(str);
 808      }
 809  
 810  
 811      c->inprogress = 1;
 812      gin.value = NULL;
 813      gin.length = 0;
 814  
 815      /* do gss-api negotiation dance here */
 816      while (1) {
 817  	krb5_storage *out, *in = krb5_storage_emem();
 818  	krb5_data data;
 819  
 820  	maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL,
 821  					&c->ctx, target, GSS_KRB5_MECHANISM,
 822  					GSS_C_MUTUAL_FLAG|GSS_C_INTEG_FLAG|GSS_C_CONF_FLAG,
 823  					GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS,
 824  					&gin, NULL, &gout, &ret_flags, NULL);
 825  	if (gin.value) {
 826  	    free(gin.value);
 827  	    gin.value = NULL;
 828  	}
 829  
 830  	if (GSS_ERROR(maj_stat)) {
 831  	    krb5_storage_free(in);
 832  	    if (!try_kadmin_admin) {
 833  		try_kadmin_admin = 1;
 834  		goto try_again;
 835  	    }
 836  	    krb5_set_error_message(context, EINVAL,
 837  				   "init_sec_context failed with %d/%d",
 838  				   maj_stat, min_stat);
 839  	    return EINVAL;
 840  	} else if (maj_stat & GSS_S_CONTINUE_NEEDED) {
 841  	    INSIST(!c->done);
 842  	} else {
 843  	    INSIST(maj_stat == GSS_S_COMPLETE);
 844  	    INSIST((ret_flags & GSS_C_MUTUAL_FLAG) != 0);
 845  	    if (c->done) {
 846  		verify_seq_num(c, seq_window, &c->last_cred);
 847  		krb5_data_free(&c->last_cred);
 848  		break;
 849  	    }
 850  
 851  	    c->done = 1;
 852  	}
 853  
 854  	data.data = gout.value;
 855  	data.length = gout.length;
 856  
 857  	CHECK(_kadm5_xdr_store_data_xdr(in, data));
 858  	gss_release_buffer(&min_stat, &gout);
 859  	krb5_storage_to_data(in, &data);
 860  	krb5_storage_free(in);
 861  
 862  	ret = xdr_process_request(context, c, PROC_NULL, &data, &out);
 863  	krb5_data_free(&data);
 864  	if (ret)
 865  	    return ret;
 866  
 867  	CHECK(_kadm5_xdr_ret_gss_init_res(out, &c->handle,
 868  					  &maj_stat2, &min_stat,
 869  					  &seq_window, &data));
 870  	krb5_storage_free(out);
 871  
 872  	gin.length = data.length;
 873  	gin.value = data.data;
 874  
 875  	if (GSS_ERROR(maj_stat2)) {
 876  	    krb5_data_free(&data);
 877  	    krb5_set_error_message(context, EINVAL,
 878  				   "server sent a failure code: %d/%d",
 879  				   maj_stat2, min_stat);
 880  	    return EINVAL;
 881  	} else if (maj_stat2 & GSS_S_CONTINUE_NEEDED) {
 882  	    INSIST(!c->done);
 883  
 884  	} else {
 885  	    INSIST(maj_stat2 == GSS_S_COMPLETE);
 886  	    if (c->done) {
 887  		verify_seq_num(c, seq_window, &c->last_cred);
 888  		krb5_data_free(&c->last_cred);
 889  		break;
 890  	    }
 891  	    c->done = 1;
 892  	}
 893      }
 894  
 895      c->inprogress = 0;
 896  
 897      *client = c;
 898      return 0;
 899  }
 900  
 901  
 902  static void
 903  set_funcs(kadm5_mit_context *c)
 904  {
 905  #define SET(C, F) (C)->funcs.F = kadm5_mit_ ## F
 906      SET(c, chpass_principal);
 907      SET(c, chpass_principal_with_key);
 908      SET(c, create_principal);
 909      SET(c, delete_principal);
 910      SET(c, destroy);
 911      SET(c, flush);
 912      SET(c, get_principal);
 913      SET(c, get_principals);
 914      SET(c, get_privs);
 915      SET(c, modify_principal);
 916      SET(c, randkey_principal);
 917      SET(c, rename_principal);
 918  }
 919  
 920  /*
 921   *
 922   */
 923  
 924  kadm5_ret_t
 925  kadm5_mit_init_with_password_ctx(krb5_context context,
 926  				 const char *client_name,
 927  				 const char *password,
 928  				 kadm5_config_params *params,
 929  				 unsigned long struct_version,
 930  				 unsigned long api_version,
 931  				 void **server_handle)
 932  {
 933      kadm5_ret_t ret;
 934      kadm5_mit_context *ctx;
 935      struct grpc_client *c = NULL;
 936      char *colon;
 937  
 938      ctx = malloc(sizeof(*ctx));
 939      if(ctx == NULL)
 940  	return ENOMEM;
 941      memset(ctx, 0, sizeof(*ctx));
 942      set_funcs(ctx);
 943  
 944      ctx->context = context;
 945      ctx->my_context = 0;
 946      krb5_add_et_list (context, initialize_kadm5_error_table_r);
 947  
 948      if (client_name) {
 949  	ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
 950      } else {
 951  	krb5_ccache id;
 952  
 953  	ret = _kadm5_c_get_cache_principal(context, &id, &ctx->caller);
 954  	if (ret) {
 955  	    const char *user;
 956  
 957  	    user = get_default_username ();
 958  	    if(user == NULL) {
 959  		krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name");
 960  		ret = KADM5_FAILURE;
 961  	    } else {
 962  		ret = krb5_make_principal(context, &ctx->caller,
 963  					  NULL, user, "admin", NULL);
 964  	    }
 965  	} else if (id) {
 966  	    krb5_cc_close(context, id);
 967  	}
 968      }
 969      if(ret) {
 970  	free(ctx);
 971  	return ret;
 972      }
 973  
 974      if(params->mask & KADM5_CONFIG_REALM) {
 975  	ret = 0;
 976  	ctx->realm = strdup(params->realm);
 977  	if (ctx->realm == NULL)
 978  	    ret = ENOMEM;
 979      } else
 980  	ret = krb5_get_default_realm(ctx->context, &ctx->realm);
 981      if (ret) {
 982  	free(ctx);
 983  	return ret;
 984      }
 985  
 986      if(params->mask & KADM5_CONFIG_ADMIN_SERVER)
 987  	ctx->admin_server = strdup(params->admin_server);
 988      else {
 989  	krb5_krbhst_handle handle = NULL;
 990  	char host[MAXHOSTNAMELEN];
 991  
 992  	ret = krb5_krbhst_init(context, ctx->realm, KRB5_KRBHST_ADMIN, &handle);
 993  	if (ret == 0)
 994  	    ret = krb5_krbhst_next_as_string(context, handle, host, sizeof(host));
 995  	krb5_krbhst_free(context, handle);
 996  	if (ret) {
 997  	    free(ctx->realm);
 998  	    free(ctx);
 999  	    return ret;
1000  	}
1001  	ctx->admin_server = strdup(host);
1002      }
1003  
1004      if (ctx->admin_server == NULL) {
1005  	free(ctx->realm);
1006  	free(ctx);
1007  	return ENOMEM;
1008      }
1009      colon = strchr (ctx->admin_server, ':');
1010      if (colon != NULL)
1011  	*colon++ = '\0';
1012  
1013      ctx->kadmind_port = 0;
1014  
1015      if(params->mask & KADM5_CONFIG_KADMIND_PORT)
1016  	ctx->kadmind_port = params->kadmind_port;
1017      else if (colon != NULL) {
1018  	char *end;
1019  	ctx->kadmind_port = htons(strtol (colon, &end, 0));
1020      }
1021      if (ctx->kadmind_port == 0)
1022  	ctx->kadmind_port = krb5_getportbyname (context, "kerberos-adm",
1023  						   "tcp", 749);
1024  
1025      ret = xdr_setup_connection(context, KADM5_KADMIN_SERVICE,
1026  			       ctx->admin_server, ctx->kadmind_port, &c);
1027      if (ret) {
1028  	free(ctx->realm);
1029  	free(ctx);
1030  	return ret;
1031      }
1032  
1033      ctx->gsscontext = c;
1034  
1035      *server_handle = ctx;
1036      return 0;
1037  }