static int dump_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v) { krb5_error_code ret; krb5_storage *dump = (krb5_storage *)v; krb5_storage *sp; krb5_data data; ret = hdb_entry2value (context, &entry->entry, &data); if (ret) return ret; ret = krb5_data_realloc (&data, data.length + 4); if (ret) goto done; memmove ((char *)data.data + 4, data.data, data.length - 4); sp = krb5_storage_from_data(&data); if (sp == NULL) { ret = ENOMEM; goto done; } ret = krb5_store_uint32(sp, ONE_PRINC); krb5_storage_free(sp); if (ret == 0) ret = krb5_store_data(dump, data); done: krb5_data_free (&data); return ret; }
static void send_im_here(krb5_context context, int fd, krb5_auth_context auth_context) { krb5_storage *sp; krb5_data data; krb5_error_code ret; ret = krb5_data_alloc(&data, 4); if (ret) krb5_err(context, IPROPD_RESTART, ret, "send_im_here"); sp = krb5_storage_from_data (&data); if (sp == NULL) krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_data"); ret = krb5_store_uint32(sp, I_AM_HERE); krb5_storage_free(sp); if (ret == 0) { ret = krb5_write_priv_message(context, auth_context, &fd, &data); krb5_data_free(&data); if (ret) krb5_err(context, IPROPD_RESTART, ret, "krb5_write_priv_message"); if (verbose) krb5_warnx(context, "pinged master"); } return; }
static int prop_one (krb5_context context, HDB *db, hdb_entry *entry, void *v) { krb5_error_code ret; krb5_storage *sp; krb5_data data; struct slave *slave = (struct slave *)v; ret = hdb_entry2value (context, entry, &data); if (ret) return ret; ret = krb5_data_realloc (&data, data.length + 4); if (ret) { krb5_data_free (&data); return ret; } memmove ((char *)data.data + 4, data.data, data.length - 4); sp = krb5_storage_from_data(&data); if (sp == NULL) { krb5_data_free (&data); return ENOMEM; } krb5_store_int32(sp, ONE_PRINC); krb5_storage_free(sp); ret = krb5_write_priv_message (context, slave->ac, &slave->fd, &data); krb5_data_free (&data); return ret; }
kadm5_ret_t _kadm5_unmarshal_params(krb5_context context, krb5_data *in, kadm5_config_params *params) { krb5_error_code ret; krb5_storage *sp; int32_t mask; sp = krb5_storage_from_data(in); if (sp == NULL) return ENOMEM; ret = krb5_ret_int32(sp, &mask); if (ret) goto out; params->mask = mask; if(params->mask & KADM5_CONFIG_REALM) ret = krb5_ret_string(sp, ¶ms->realm); out: krb5_storage_free(sp); return ret; }
krb5_error_code kcm_dispatch(krb5_context context, kcm_client *client, krb5_data *req_data, krb5_data *resp_data) { krb5_error_code ret; kcm_method method; krb5_storage *req_sp = NULL; krb5_storage *resp_sp = NULL; u_int16_t opcode; resp_sp = krb5_storage_emem(); if (resp_sp == NULL) { return ENOMEM; } if (client->pid == -1) { kcm_log(0, "Client had invalid process number"); ret = KRB5_FCC_INTERNAL; goto out; } req_sp = krb5_storage_from_data(req_data); if (req_sp == NULL) { kcm_log(0, "Process %d: failed to initialize storage from data", client->pid); ret = KRB5_CC_IO; goto out; } krb5_ret_int16(req_sp, &opcode); if (opcode >= sizeof(kcm_ops)/sizeof(kcm_ops[0])) { kcm_log(0, "Process %d: invalid operation code %d", client->pid, opcode); ret = KRB5_FCC_INTERNAL; goto out; } method = kcm_ops[opcode].method; /* seek past place for status code */ krb5_storage_seek(resp_sp, 4, SEEK_SET); ret = (*method)(context, client, opcode, req_sp, resp_sp); out: if (req_sp != NULL) { krb5_storage_free(req_sp); } krb5_storage_seek(resp_sp, 0, SEEK_SET); krb5_store_int32(resp_sp, ret); ret = krb5_storage_to_data(resp_sp, resp_data); krb5_storage_free(resp_sp); return ret; }
kadm5_ret_t kadm5_c_get_principals(void *server_handle, const char *expression, char ***princs, int *count) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1024]; int32_t tmp; krb5_data reply; ret = _kadm5_connect(server_handle); if(ret) return ret; sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) return ENOMEM; krb5_store_int32(sp, kadm_get_princs); krb5_store_int32(sp, expression != NULL); if(expression) krb5_store_string(sp, expression); ret = _kadm5_client_send(context, sp); krb5_storage_free(sp); if (ret) return ret; ret = _kadm5_client_recv(context, &reply); if(ret) return ret; sp = krb5_storage_from_data (&reply); if (sp == NULL) { krb5_data_free (&reply); return ENOMEM; } krb5_ret_int32(sp, &tmp); ret = tmp; if(ret == 0) { int i; krb5_ret_int32(sp, &tmp); *princs = calloc(tmp + 1, sizeof(**princs)); if (*princs == NULL) { ret = ENOMEM; goto out; } for(i = 0; i < tmp; i++) krb5_ret_string(sp, &(*princs)[i]); *count = tmp; } out: krb5_storage_free(sp); krb5_data_free (&reply); return ret; }
kadm5_ret_t kadm5_c_chpass_principal(void *server_handle, krb5_principal princ, int keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, const char *password) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1024]; int32_t tmp; krb5_data reply; /* * We should get around to implementing this... At the moment, the * the server side API is implemented but the wire protocol has not * been updated. */ if (n_ks_tuple > 0) return KADM5_KS_TUPLE_NOSUPP; ret = _kadm5_connect(server_handle); if(ret) return ret; sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) { krb5_clear_error_message(context->context); return ENOMEM; } krb5_store_int32(sp, kadm_chpass); krb5_store_principal(sp, princ); krb5_store_string(sp, password); krb5_store_int32(sp, keepold); /* extension */ ret = _kadm5_client_send(context, sp); krb5_storage_free(sp); if (ret) return ret; ret = _kadm5_client_recv(context, &reply); if(ret) return ret; sp = krb5_storage_from_data (&reply); if (sp == NULL) { krb5_clear_error_message(context->context); krb5_data_free (&reply); return ENOMEM; } krb5_ret_int32(sp, &tmp); krb5_clear_error_message(context->context); krb5_storage_free(sp); krb5_data_free (&reply); return tmp; }
kadm5_ret_t kadm5_c_get_privs(void *server_handle, uint32_t *privs) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1024]; int32_t tmp; krb5_data reply; *privs = 0; ret = _kadm5_connect(server_handle); if (ret) return ret; krb5_data_zero(&reply); sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) { ret = krb5_enomem(context->context); goto out_keep_error; } ret = krb5_store_int32(sp, kadm_get_privs); if (ret) goto out; ret = _kadm5_client_send(context, sp); if (ret) goto out_keep_error; ret = _kadm5_client_recv(context, &reply); if (ret) goto out_keep_error; krb5_storage_free(sp); sp = krb5_storage_from_data(&reply); if (sp == NULL) { ret = krb5_enomem(context->context); goto out_keep_error; } ret = krb5_ret_int32(sp, &tmp); if (ret == 0) ret = tmp; if (ret == 0) krb5_ret_uint32(sp, privs); out: krb5_clear_error_message(context->context); out_keep_error: krb5_storage_free(sp); krb5_data_free (&reply); return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_kcm_call(krb5_context context, krb5_storage *request, krb5_storage **response_p, krb5_data *response_data_p) { krb5_data response_data; krb5_error_code ret; int32_t status; krb5_storage *response; if (response_p != NULL) *response_p = NULL; krb5_data_zero(&response_data); ret = kcm_send_request(context, request, &response_data); if (ret) return ret; response = krb5_storage_from_data(&response_data); if (response == NULL) { krb5_data_free(&response_data); return KRB5_CC_IO; } ret = krb5_ret_int32(response, &status); if (ret) { krb5_storage_free(response); krb5_data_free(&response_data); return KRB5_CC_FORMAT; } if (status) { krb5_storage_free(response); krb5_data_free(&response_data); return status; } if (response_p != NULL) { *response_data_p = response_data; *response_p = response; return 0; } krb5_storage_free(response); krb5_data_free(&response_data); return 0; }
static krb5_error_code kcm_call(krb5_context context, krb5_kcmcache *k, krb5_storage *request, krb5_storage **response_p, krb5_data *response_data_p) { krb5_data response_data; krb5_error_code ret, status; krb5_storage *response; if (response_p != NULL) *response_p = NULL; ret = kcm_send_request(context, k, request, &response_data); if (ret) { return ret; } response = krb5_storage_from_data(&response_data); if (response == NULL) { krb5_data_free(&response_data); return KRB5_CC_IO; } ret = krb5_ret_int32(response, &status); if (ret) { krb5_storage_free(response); krb5_data_free(&response_data); return KRB5_CC_FORMAT; } if (status) { krb5_storage_free(response); krb5_data_free(&response_data); return status; } if (response_p != NULL) { *response_data_p = response_data; *response_p = response; return 0; } krb5_storage_free(response); krb5_data_free(&response_data); return 0; }
kadm5_ret_t kadm5_c_chpass_principal_with_key(void *server_handle, krb5_principal princ, int keepold, int n_key_data, krb5_key_data *key_data) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1024]; int32_t tmp; krb5_data reply; int i; ret = _kadm5_connect(server_handle); if(ret) return ret; sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) { krb5_clear_error_message(context->context); return ENOMEM; } krb5_store_int32(sp, kadm_chpass_with_key); krb5_store_principal(sp, princ); krb5_store_int32(sp, n_key_data); for (i = 0; i < n_key_data; ++i) kadm5_store_key_data (sp, &key_data[i]); krb5_store_int32(sp, keepold); /* extension */ ret = _kadm5_client_send(context, sp); krb5_storage_free(sp); if (ret) return ret; ret = _kadm5_client_recv(context, &reply); if(ret) return ret; sp = krb5_storage_from_data (&reply); if (sp == NULL) { krb5_clear_error_message(context->context); krb5_data_free (&reply); return ENOMEM; } krb5_ret_int32(sp, &tmp); krb5_clear_error_message(context->context); krb5_storage_free(sp); krb5_data_free (&reply); return tmp; }
static int addrport_print_addr (const krb5_address *addr, char *str, size_t len) { krb5_error_code ret; krb5_address addr1, addr2; uint16_t port = 0; size_t ret_len = 0, l, size = 0; krb5_storage *sp; sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address)); if (sp == NULL) return ENOMEM; /* for totally obscure reasons, these are not in network byteorder */ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */ krb5_ret_address(sp, &addr1); krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */ krb5_ret_address(sp, &addr2); krb5_storage_free(sp); if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) { unsigned long value; _krb5_get_int(addr2.address.data, &value, 2); port = value; } l = strlcpy(str, "ADDRPORT:", len); ret_len += l; if (len > l) size += l; else size = len; ret = krb5_print_address(&addr1, str + size, len - size, &l); if (ret) return ret; ret_len += l; if (len - size > l) size += l; else size = len; ret = snprintf(str + size, len - size, ",PORT=%u", port); if (ret < 0) return EINVAL; ret_len += ret; return ret_len; }
kadm5_ret_t kadm5_c_get_principal(void *server_handle, krb5_principal princ, kadm5_principal_ent_t out, uint32_t mask) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1024]; int32_t tmp; krb5_data reply; ret = _kadm5_connect(server_handle); if(ret) return ret; sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) { krb5_clear_error_message(context->context); return ENOMEM; } krb5_store_int32(sp, kadm_get); krb5_store_principal(sp, princ); krb5_store_int32(sp, mask); ret = _kadm5_client_send(context, sp); krb5_storage_free(sp); if(ret) return ret; ret = _kadm5_client_recv(context, &reply); if (ret) return ret; sp = krb5_storage_from_data (&reply); if (sp == NULL) { krb5_clear_error_message(context->context); krb5_data_free (&reply); return ENOMEM; } krb5_ret_int32(sp, &tmp); ret = tmp; krb5_clear_error_message(context->context); if(ret == 0) kadm5_ret_principal_ent(sp, out); krb5_storage_free(sp); krb5_data_free (&reply); return ret; }
krb5_error_code _kadm5_xdr_ret_gacred(krb5_data *data, struct _kadm5_xdr_gacred *gacred) { krb5_storage *sp; memset(gacred, 0, sizeof(*gacred)); sp = krb5_storage_from_data(data); INSIST(sp != NULL); CHECK(krb5_ret_uint32(sp, &gacred->version)); CHECK(krb5_ret_uint32(sp, &gacred->auth_msg)); CHECK(_kadm5_xdr_ret_data_xdr(sp, &gacred->handle)); krb5_storage_free(sp); return 0; }
kadm5_ret_t kadm5_c_chpass_principal(void *server_handle, krb5_principal princ, const char *password) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1024]; int32_t tmp; krb5_data reply; ret = _kadm5_connect(server_handle); if(ret) return ret; sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) { krb5_clear_error_message(context->context); return ENOMEM; } krb5_store_int32(sp, kadm_chpass); krb5_store_principal(sp, princ); krb5_store_string(sp, password); ret = _kadm5_client_send(context, sp); krb5_storage_free(sp); if (ret) return ret; ret = _kadm5_client_recv(context, &reply); if(ret) return ret; sp = krb5_storage_from_data (&reply); if (sp == NULL) { krb5_clear_error_message(context->context); krb5_data_free (&reply); return ENOMEM; } krb5_ret_int32(sp, &tmp); krb5_clear_error_message(context->context); krb5_storage_free(sp); krb5_data_free (&reply); return tmp; }
kadm5_ret_t kadm5_c_rename_principal(void *server_handle, krb5_principal source, krb5_principal target) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1024]; int32_t tmp; krb5_data reply; ret = _kadm5_connect(server_handle); if(ret) return ret; sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) return ENOMEM; krb5_store_int32(sp, kadm_rename); krb5_store_principal(sp, source); krb5_store_principal(sp, target); ret = _kadm5_client_send(context, sp); krb5_storage_free(sp); if (ret) return ret; ret = _kadm5_client_recv(context, &reply); if(ret) return ret; sp = krb5_storage_from_data (&reply); if (sp == NULL) { krb5_data_free (&reply); return ENOMEM; } krb5_ret_int32(sp, &tmp); ret = tmp; krb5_storage_free(sp); krb5_data_free (&reply); return ret; }
static void send_im_here (krb5_context context, int fd, krb5_auth_context auth_context) { krb5_storage *sp; krb5_data data; int ret; ret = krb5_data_alloc (&data, 4); if (ret) krb5_err (context, 1, ret, "send_im_here"); sp = krb5_storage_from_data (&data); if (sp == NULL) krb5_errx (context, 1, "krb5_storage_from_data"); krb5_store_int32(sp, I_AM_HERE); krb5_storage_free(sp); ret = krb5_write_priv_message(context, auth_context, &fd, &data); krb5_data_free(&data); if (ret) krb5_err (context, 1, ret, "krb5_write_priv_message"); }
static int send_diffs (krb5_context context, slave *s, int log_fd, const char *database, u_int32_t current_version) { krb5_storage *sp; u_int32_t ver; time_t timestamp; enum kadm_ops op; u_int32_t len; off_t right, left; krb5_data data; int ret = 0; if (s->version == current_version) return 0; if (s->flags & SLAVE_F_DEAD) return 0; sp = kadm5_log_goto_end (log_fd); right = krb5_storage_seek(sp, 0, SEEK_CUR); for (;;) { if (kadm5_log_previous (sp, &ver, ×tamp, &op, &len)) abort (); left = krb5_storage_seek(sp, -16, SEEK_CUR); if (ver == s->version) return 0; if (ver == s->version + 1) break; if (left == 0) return send_complete (context, s, database, current_version); } ret = krb5_data_alloc (&data, right - left + 4); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); slave_dead(s); return 1; } krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); krb5_storage_free(sp); sp = krb5_storage_from_data (&data); if (sp == NULL) { krb5_warnx (context, "send_diffs: krb5_storage_from_data"); slave_dead(s); return 1; } krb5_store_int32 (sp, FOR_YOU); krb5_storage_free(sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); slave_dead(s); return 1; } slave_seen(s); return 0; }
static krb5_error_code mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry) { krb5_error_code ret; krb5_storage *sp; uint32_t u32; uint16_t u16, num_keys, num_tl; size_t i, j; char *p; sp = krb5_storage_from_data(data); if (sp == NULL) { krb5_set_error_message(context, ENOMEM, "out of memory"); return ENOMEM; } krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); /* * 16: baselength * * The story here is that these 16 bits have to be a constant: * KDB_V1_BASE_LENGTH. Once upon a time a different value here * would have been used to indicate the presence of "extra data" * between the "base" contents and the {principal name, TL data, * keys} that follow it. Nothing supports such "extra data" * nowadays, so neither do we here. * * XXX But... surely we ought to log about this extra data, or skip * it, or something, in case anyone has MIT KDBs with ancient * entries in them... Logging would allow the admin to know which * entries to dump with MIT krb5's kdb5_util. */ CHECK(ret = krb5_ret_uint16(sp, &u16)); if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; } /* 32: attributes */ CHECK(ret = krb5_ret_uint32(sp, &u32)); entry->flags.postdate = !(u32 & KRB5_KDB_DISALLOW_POSTDATED); entry->flags.forwardable = !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE); entry->flags.initial = !!(u32 & KRB5_KDB_DISALLOW_TGT_BASED); entry->flags.renewable = !(u32 & KRB5_KDB_DISALLOW_RENEWABLE); entry->flags.proxiable = !(u32 & KRB5_KDB_DISALLOW_PROXIABLE); /* DUP_SKEY */ entry->flags.invalid = !!(u32 & KRB5_KDB_DISALLOW_ALL_TIX); entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH); entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH); entry->flags.server = !(u32 & KRB5_KDB_DISALLOW_SVR); entry->flags.change_pw = !!(u32 & KRB5_KDB_PWCHANGE_SERVICE); entry->flags.client = 1; /* XXX */ /* 32: max time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_life = malloc(sizeof(*entry->max_life)); *entry->max_life = u32; } /* 32: max renewable time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_renew = malloc(sizeof(*entry->max_renew)); *entry->max_renew = u32; } /* 32: client expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->valid_end = malloc(sizeof(*entry->valid_end)); *entry->valid_end = u32; } /* 32: passwd expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->pw_end = malloc(sizeof(*entry->pw_end)); *entry->pw_end = u32; } /* 32: last successful passwd */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: last failed attempt */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: num of failed attempts */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 16: num tl data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_tl = u16; /* 16: num key data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_keys = u16; /* 16: principal length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* length: principal */ { /* * Note that the principal name includes the NUL in the entry, * but we don't want to take chances, so we add an extra NUL. */ p = malloc(u16 + 1); if (p == NULL) { ret = ENOMEM; goto out; } krb5_storage_read(sp, p, u16); p[u16] = '\0'; CHECK(ret = krb5_parse_name(context, p, &entry->principal)); free(p); } /* for num tl data times 16: tl data type 16: tl data length length: length */ for (i = 0; i < num_tl; i++) { /* 16: TL data type */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* 16: TL data length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } /* * for num key data times * 16: "version" * 16: kvno * for version times: * 16: type * 16: length * length: keydata * * "version" here is really 1 or 2, the first meaning there's only * keys for this kvno, the second meaning there's keys and salt[s?]. * That's right... hold that gag reflex, you can do it. */ for (i = 0; i < num_keys; i++) { int keep = 0; uint16_t version; void *ptr; CHECK(ret = krb5_ret_uint16(sp, &u16)); version = u16; CHECK(ret = krb5_ret_uint16(sp, &u16)); /* * First time through, and until we find one matching key, * entry->kvno == 0. */ if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) { keep = 1; entry->kvno = u16; /* * Found a higher kvno than earlier, so free the old highest * kvno keys. * * XXX Of course, we actually want to extract the old kvnos * as well, for some of the kadm5 APIs. We shouldn't free * these keys, but keep them elsewhere. */ for (j = 0; j < entry->keys.len; j++) free_Key(&entry->keys.val[j]); free(entry->keys.val); entry->keys.len = 0; entry->keys.val = NULL; } else if (entry->kvno == u16) /* Accumulate keys */ keep = 1; if (keep) { Key *k; ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1)); if (ptr == NULL) { ret = ENOMEM; goto out; } entry->keys.val = ptr; /* k points to current Key */ k = &entry->keys.val[entry->keys.len]; memset(k, 0, sizeof(*k)); entry->keys.len += 1; k->mkvno = malloc(sizeof(*k->mkvno)); if (k->mkvno == NULL) { ret = ENOMEM; goto out; } *k->mkvno = 1; for (j = 0; j < version; j++) { uint16_t type; CHECK(ret = krb5_ret_uint16(sp, &type)); CHECK(ret = krb5_ret_uint16(sp, &u16)); if (j == 0) { /* This "version" means we have a key */ k->key.keytype = type; if (u16 < 2) { ret = EINVAL; goto out; } /* * MIT stores keys encrypted keys as {16-bit length * of plaintext key, {encrypted key}}. The reason * for this is that the Kerberos cryptosystem is not * length-preserving. Heimdal's approach is to * truncate the plaintext to the expected length of * the key given its enctype, so we ignore this * 16-bit length-of-plaintext-key field. */ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */ k->key.keyvalue.length = u16 - 2; /* adjust cipher len */ k->key.keyvalue.data = malloc(k->key.keyvalue.length); krb5_storage_read(sp, k->key.keyvalue.data, k->key.keyvalue.length); } else if (j == 1) { /* This "version" means we have a salt */ k->salt = calloc(1, sizeof(*k->salt)); if (k->salt == NULL) { ret = ENOMEM; goto out; } k->salt->type = type; if (u16 != 0) { k->salt->salt.data = malloc(u16); if (k->salt->salt.data == NULL) { ret = ENOMEM; goto out; } k->salt->salt.length = u16; krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length); } fix_salt(context, entry, entry->keys.len - 1); } else { /* * Whatever this "version" might be, we skip it * * XXX A krb5.conf parameter requesting that we log * about strangeness like this, or return an error * from here, might be nice. */ krb5_storage_seek(sp, u16, SEEK_CUR); } } } else { /* * XXX For now we skip older kvnos, but we should extract * them... */ for (j = 0; j < version; j++) { /* enctype */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* encrypted key (or plaintext salt) */ CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } } } if (entry->kvno == 0 && kvno != 0) { ret = HDB_ERR_NOT_FOUND_HERE; goto out; } return 0; out: if (ret == HEIM_ERR_EOF) /* Better error code than "end of file" */ ret = HEIM_ERR_BAD_HDBENT_ENCODING; return ret; }
static int send_diffs (kadm5_server_context *server_context, slave *s, int log_fd, const char *database, uint32_t current_version, uint32_t current_tstamp) { krb5_context context = server_context->context; krb5_storage *sp; uint32_t ver, initial_version, initial_version2; uint32_t initial_tstamp, initial_tstamp2; enum kadm_ops op; uint32_t len; off_t right, left; krb5_ssize_t bytes; krb5_data data; int ret = 0; if (s->flags & SLAVE_F_DEAD) { krb5_warnx(context, "not sending diffs to dead slave %s", s->name); return 0; } if (s->version == current_version) { char buf[4]; sp = krb5_storage_from_mem(buf, 4); if (sp == NULL) krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_mem"); ret = krb5_store_uint32(sp, YOU_HAVE_LAST_VERSION); krb5_storage_free(sp); data.data = buf; data.length = 4; if (ret == 0) { ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); if (ret) { krb5_warn(context, ret, "send_diffs: failed to send to slave"); slave_dead(context, s); } krb5_warnx(context, "slave %s in sync already at version %ld", s->name, (long)s->version); } return ret; } if (verbose) krb5_warnx(context, "sending diffs to live-seeming slave %s", s->name); /* * XXX The code that makes the diffs should be made a separate function, * then error handling (send_are_you_there() or slave_dead()) can be done * here. */ if (flock(log_fd, LOCK_SH) == -1) { krb5_warn(context, errno, "could not obtain shared lock on log file"); send_are_you_there(context, s); return errno; } ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST, &initial_version, &initial_tstamp); sp = kadm5_log_goto_end(server_context, log_fd); flock(log_fd, LOCK_UN); if (ret) { if (sp != NULL) krb5_storage_free(sp); krb5_warn(context, ret, "send_diffs: failed to read log"); send_are_you_there(context, s); return ret; } if (sp == NULL) { send_are_you_there(context, s); krb5_warn(context, errno ? errno : EINVAL, "send_diffs: failed to read log"); return errno ? errno : EINVAL; } /* * We're not holding any locks here, so we can't prevent truncations. * * We protect against this by re-checking that the initial version and * timestamp are the same before and after this loop. */ right = krb5_storage_seek(sp, 0, SEEK_CUR); if (right == (off_t)-1) { krb5_storage_free(sp); send_are_you_there(context, s); return errno; } for (;;) { ret = kadm5_log_previous (context, sp, &ver, NULL, &op, &len); if (ret) krb5_err(context, IPROPD_RESTART, ret, "send_diffs: failed to find previous entry"); left = krb5_storage_seek(sp, -16, SEEK_CUR); if (left == (off_t)-1) { krb5_storage_free(sp); send_are_you_there(context, s); return errno; } if (ver == s->version + 1) break; /* * We don't expect to reach the slave's version, except when it is * starting empty with the uber record. */ if (ver == s->version && !(ver == 0 && op == kadm_nop)) { /* * This shouldn't happen, but recall we're not holding a lock on * the log. */ krb5_storage_free(sp); krb5_warnx(context, "iprop log truncated while sending diffs to " "slave?? ver = %lu", (unsigned long)ver); send_are_you_there(context, s); return 0; } /* If we've reached the uber record, send the complete database */ if (left == 0 || (ver == 0 && op == kadm_nop)) { krb5_storage_free(sp); krb5_warnx(context, "slave %s (version %lu) out of sync with master " "(first version in log %lu), sending complete database", s->name, (unsigned long)s->version, (unsigned long)ver); return send_complete (context, s, database, current_version, ver, initial_tstamp); } } assert(ver == s->version + 1); krb5_warnx(context, "syncing slave %s from version %lu to version %lu", s->name, (unsigned long)s->version, (unsigned long)current_version); ret = krb5_data_alloc (&data, right - left + 4); if (ret) { krb5_storage_free(sp); krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); send_are_you_there(context, s); return 1; } bytes = krb5_storage_read(sp, (char *)data.data + 4, data.length - 4); krb5_storage_free(sp); if (bytes != data.length - 4) { krb5_warnx(context, "iprop log truncated while sending diffs to " "slave?? ver = %lu", (unsigned long)ver); send_are_you_there(context, s); return 1; } /* * Check that we have the same log initial version and timestamp now as * when we dropped the shared lock on the log file! Else we could be * sending garbage to the slave. */ if (flock(log_fd, LOCK_SH) == -1) { krb5_warn(context, errno, "could not obtain shared lock on log file"); send_are_you_there(context, s); return 1; } ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST, &initial_version2, &initial_tstamp2); flock(log_fd, LOCK_UN); if (ret) { krb5_warn(context, ret, "send_diffs: failed to read log while producing diffs"); send_are_you_there(context, s); return 1; } if (initial_version != initial_version2 || initial_tstamp != initial_tstamp2) { krb5_warn(context, ret, "send_diffs: log truncated while producing diffs"); send_are_you_there(context, s); return 1; } sp = krb5_storage_from_data (&data); if (sp == NULL) { krb5_warnx (context, "send_diffs: krb5_storage_from_data"); send_are_you_there(context, s); return 1; } krb5_store_uint32 (sp, FOR_YOU); krb5_storage_free(sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); slave_dead(context, s); return 1; } slave_seen(s); s->version = current_version; krb5_warnx(context, "slave %s is now up to date (%u)", s->name, s->version); return 0; }
krb5_error_code KRB5_LIB_FUNCTION _krb5_krb_rd_req(krb5_context context, krb5_data *authent, const char *service, const char *instance, const char *local_realm, int32_t from_addr, const krb5_keyblock *key, struct _krb5_krb_auth_data *ad) { krb5_error_code ret; krb5_storage *sp; krb5_data ticket, eaut, aut; krb5_ssize_t size; int little_endian; int8_t pvno; int8_t type; int8_t s_kvno; uint8_t ticket_length; uint8_t eaut_length; uint8_t time_5ms; char *realm = NULL; char *sname = NULL; char *sinstance = NULL; char *r_realm = NULL; char *r_name = NULL; char *r_instance = NULL; uint32_t r_time_sec; /* Coarse time from authenticator */ unsigned long delta_t; /* Time in authenticator - local time */ long tkt_age; /* Age of ticket */ struct timeval tv; krb5_data_zero(&ticket); krb5_data_zero(&eaut); krb5_data_zero(&aut); sp = krb5_storage_from_data(authent); if (sp == NULL) { krb5_set_error_string(context, "alloc: out of memory"); return ENOMEM; } krb5_storage_set_eof_code(sp, EINVAL); /* XXX */ ret = krb5_ret_int8(sp, &pvno); if (ret) goto error; if (pvno != KRB_PROT_VERSION) { ret = EINVAL; /* XXX */ goto error; } ret = krb5_ret_int8(sp, &type); if (ret) goto error; little_endian = type & 1; type &= ~1; if(type != AUTH_MSG_APPL_REQUEST && type != AUTH_MSG_APPL_REQUEST_MUTUAL) { ret = EINVAL; /* RD_AP_MSG_TYPE */ goto error; } RCHECK(ret, krb5_ret_int8(sp, &s_kvno), error); RCHECK(ret, get_v4_stringz(sp, &realm, REALM_SZ), error); RCHECK(ret, krb5_ret_uint8(sp, &ticket_length), error); RCHECK(ret, krb5_ret_uint8(sp, &eaut_length), error); RCHECK(ret, krb5_data_alloc(&ticket, ticket_length), error); size = krb5_storage_read(sp, ticket.data, ticket.length); if (size != ticket.length) { ret = EINVAL; goto error; } /* Decrypt and take apart ticket */ ret = _krb5_krb_decomp_ticket(context, &ticket, key, local_realm, &sname, &sinstance, ad); if (ret) goto error; RCHECK(ret, krb5_data_alloc(&eaut, eaut_length), error); size = krb5_storage_read(sp, eaut.data, eaut.length); if (size != eaut.length) { ret = EINVAL; goto error; } krb5_storage_free(sp); sp = NULL; ret = decrypt_etext(context, &ad->session, &eaut, &aut); if (ret) goto error; sp = krb5_storage_from_data(&aut); if (sp == NULL) { krb5_set_error_string(context, "alloc: out of memory"); ret = ENOMEM; goto error; } if (little_endian) krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); else krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE); RCHECK(ret, get_v4_stringz(sp, &r_name, ANAME_SZ), error); RCHECK(ret, get_v4_stringz(sp, &r_instance, INST_SZ), error); RCHECK(ret, get_v4_stringz(sp, &r_realm, REALM_SZ), error); RCHECK(ret, krb5_ret_uint32(sp, &ad->checksum), error); RCHECK(ret, krb5_ret_uint8(sp, &time_5ms), error); RCHECK(ret, krb5_ret_uint32(sp, &r_time_sec), error); if (strcmp(ad->pname, r_name) != 0 || strcmp(ad->pinst, r_instance) != 0 || strcmp(ad->prealm, r_realm) != 0) { ret = EINVAL; /* RD_AP_INCON */ goto error; } if (from_addr && from_addr != ad->address) { ret = EINVAL; /* RD_AP_BADD */ goto error; } gettimeofday(&tv, NULL); delta_t = abs((int)(tv.tv_sec - r_time_sec)); if (delta_t > CLOCK_SKEW) { ret = EINVAL; /* RD_AP_TIME */ goto error; } /* Now check for expiration of ticket */ tkt_age = tv.tv_sec - ad->time_sec; if ((tkt_age < 0) && (-tkt_age > CLOCK_SKEW)) { ret = EINVAL; /* RD_AP_NYV */ goto error; } if (tv.tv_sec > _krb5_krb_life_to_time(ad->time_sec, ad->life)) { ret = EINVAL; /* RD_AP_EXP */ goto error; } ret = 0; error: krb5_data_free(&ticket); krb5_data_free(&eaut); krb5_data_free(&aut); if (realm) free(realm); if (sname) free(sname); if (sinstance) free(sinstance); if (r_name) free(r_name); if (r_instance) free(r_instance); if (r_realm) free(r_realm); if (sp) krb5_storage_free(sp); if (ret) krb5_clear_error_string(context); return ret; }
krb5_error_code KRB5_LIB_FUNCTION _krb5_krb_decomp_ticket(krb5_context context, const krb5_data *enc_ticket, const krb5_keyblock *key, const char *local_realm, char **sname, char **sinstance, struct _krb5_krb_auth_data *ad) { krb5_error_code ret; krb5_ssize_t size; krb5_storage *sp = NULL; krb5_data ticket; unsigned char des_key[8]; memset(ad, 0, sizeof(*ad)); krb5_data_zero(&ticket); *sname = NULL; *sinstance = NULL; RCHECK(ret, decrypt_etext(context, key, enc_ticket, &ticket), error); sp = krb5_storage_from_data(&ticket); if (sp == NULL) { krb5_data_free(&ticket); krb5_set_error_string(context, "alloc: out of memory"); return ENOMEM; } krb5_storage_set_eof_code(sp, EINVAL); /* XXX */ RCHECK(ret, krb5_ret_int8(sp, &ad->k_flags), error); RCHECK(ret, get_v4_stringz(sp, &ad->pname, ANAME_SZ), error); RCHECK(ret, get_v4_stringz(sp, &ad->pinst, INST_SZ), error); RCHECK(ret, get_v4_stringz(sp, &ad->prealm, REALM_SZ), error); RCHECK(ret, krb5_ret_uint32(sp, &ad->address), error); size = krb5_storage_read(sp, des_key, sizeof(des_key)); if (size != sizeof(des_key)) { ret = EINVAL; /* XXX */ goto error; } RCHECK(ret, krb5_ret_uint8(sp, &ad->life), error); if (ad->k_flags & 1) krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); else krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE); RCHECK(ret, krb5_ret_uint32(sp, &ad->time_sec), error); RCHECK(ret, get_v4_stringz(sp, sname, ANAME_SZ), error); RCHECK(ret, get_v4_stringz(sp, sinstance, INST_SZ), error); ret = krb5_keyblock_init(context, ETYPE_DES_PCBC_NONE, des_key, sizeof(des_key), &ad->session); if (ret) goto error; if (strlen(ad->prealm) == 0) { free(ad->prealm); ad->prealm = strdup(local_realm); if (ad->prealm == NULL) { ret = ENOMEM; goto error; } } error: memset(des_key, 0, sizeof(des_key)); if (sp) krb5_storage_free(sp); krb5_data_free(&ticket); if (ret) { if (*sname) { free(*sname); *sname = NULL; } if (*sinstance) { free(*sinstance); *sinstance = NULL; } _krb5_krb_free_auth_data(context, ad); krb5_set_error_string(context, "Failed to decode v4 ticket"); } return ret; }
static krb5_error_code receive_everything (krb5_context context, int fd, kadm5_server_context *server_context, krb5_auth_context auth_context) { int ret; krb5_data data; int32_t vno = 0; int32_t opcode; krb5_storage *sp; char *dbname; HDB *mydb; krb5_warnx(context, "receive complete database"); asprintf(&dbname, "%s-NEW", server_context->db->hdb_name); ret = hdb_create(context, &mydb, dbname); if(ret) krb5_err(context,1, ret, "hdb_create"); free(dbname); ret = hdb_set_master_keyfile (context, mydb, server_context->config.stash_file); if(ret) krb5_err(context,1, ret, "hdb_set_master_keyfile"); /* I really want to use O_EXCL here, but given that I can't easily clean up on error, I won't */ ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); sp = NULL; do { ret = krb5_read_priv_message(context, auth_context, &fd, &data); if (ret) { krb5_warn (context, ret, "krb5_read_priv_message"); goto cleanup; } sp = krb5_storage_from_data (&data); if (sp == NULL) krb5_errx (context, 1, "krb5_storage_from_data"); krb5_ret_int32 (sp, &opcode); if (opcode == ONE_PRINC) { krb5_data fake_data; hdb_entry_ex entry; krb5_storage_free(sp); fake_data.data = (char *)data.data + 4; fake_data.length = data.length - 4; memset(&entry, 0, sizeof(entry)); ret = hdb_value2entry (context, &fake_data, &entry.entry); if (ret) krb5_err (context, 1, ret, "hdb_value2entry"); ret = mydb->hdb_store(server_context->context, mydb, 0, &entry); if (ret) krb5_err (context, 1, ret, "hdb_store"); hdb_free_entry (context, &entry); krb5_data_free (&data); } else if (opcode == NOW_YOU_HAVE) ; else krb5_errx (context, 1, "strange opcode %d", opcode); } while (opcode == ONE_PRINC); if (opcode != NOW_YOU_HAVE) krb5_errx (context, 1, "receive_everything: strange %d", opcode); krb5_ret_int32 (sp, &vno); krb5_storage_free(sp); ret = kadm5_log_reinit (server_context); if (ret) krb5_err(context, 1, ret, "kadm5_log_reinit"); ret = kadm5_log_set_version (server_context, vno - 1); if (ret) krb5_err (context, 1, ret, "kadm5_log_set_version"); ret = kadm5_log_nop (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_nop"); ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name); if (ret) krb5_err (context, 1, ret, "db->rename"); cleanup: krb5_data_free (&data); ret = mydb->hdb_close (context, mydb); if (ret) krb5_err (context, 1, ret, "db->close"); ret = mydb->hdb_destroy (context, mydb); if (ret) krb5_err (context, 1, ret, "db->destroy"); krb5_warnx(context, "receive complete database, version %ld", (long)vno); return ret; }
static kadm5_ret_t kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, krb5_data *in, krb5_data *out) { kadm5_ret_t ret; int32_t cmd, mask, tmp; kadm5_server_context *contextp = kadm_handlep; char client[128], name[128], name2[128]; const char *op = ""; krb5_principal princ, princ2; kadm5_principal_ent_rec ent; char *password, *expression; krb5_keyblock *new_keys; krb5_key_salt_tuple *ks_tuple = NULL; krb5_boolean keepold = FALSE; int n_ks_tuple = 0; int n_keys; char **princs; int n_princs; int keys_ok = 0; krb5_storage *sp; krb5_unparse_name_fixed(contextp->context, contextp->caller, client, sizeof(client)); sp = krb5_storage_from_data(in); if (sp == NULL) krb5_errx(contextp->context, 1, "out of memory"); krb5_ret_int32(sp, &cmd); switch(cmd){ case kadm_get:{ op = "GET"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; ret = krb5_ret_int32(sp, &mask); if(ret){ krb5_free_principal(contextp->context, princ); goto fail; } mask |= KADM5_PRINCIPAL; krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); /* If the caller doesn't have KADM5_PRIV_GET, we're done. */ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ); if (ret) { krb5_free_principal(contextp->context, princ); goto fail; } /* Then check to see if it is ok to return keys */ if ((mask & KADM5_KEY_DATA) != 0) { ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET_KEYS, princ); if (ret == 0) { keys_ok = 1; } else if ((mask == (KADM5_PRINCIPAL|KADM5_KEY_DATA)) || (mask == (KADM5_PRINCIPAL|KADM5_KVNO|KADM5_KEY_DATA))) { /* * Requests for keys will get bogus keys, which is useful if * the client just wants to see what (kvno, enctype)s the * principal has keys for, but terrible if the client wants to * write the keys into a keytab or modify the principal and * write the bogus keys back to the server. * * We use a heuristic to detect which case we're handling here. * If the client only asks for the flags in the above * condition, then it's very likely a kadmin ext_keytab, * add_enctype, or other request that should not see bogus * keys. We deny them. * * The kadmin get command can be coaxed into making a request * with the same mask. But the default long and terse output * modes request other things too, so in all likelihood this * heuristic will not hurt any kadmin get uses. */ krb5_free_principal(contextp->context, princ); goto fail; } } ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if (ret == 0){ if (keys_ok) kadm5_store_principal_ent(sp, &ent); else kadm5_store_principal_ent_nokeys(sp, &ent); kadm5_free_principal_ent(kadm_handlep, &ent); } krb5_free_principal(contextp->context, princ); break; } case kadm_delete:{ op = "DELETE"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ); if(ret){ krb5_free_principal(contextp->context, princ); goto fail; } ret = kadm5_delete_principal(kadm_handlep, princ); krb5_free_principal(contextp->context, princ); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_create:{ op = "CREATE"; ret = kadm5_ret_principal_ent(sp, &ent); if(ret) goto fail; ret = krb5_ret_int32(sp, &mask); if(ret){ kadm5_free_principal_ent(contextp->context, &ent); goto fail; } ret = krb5_ret_string(sp, &password); if(ret){ kadm5_free_principal_ent(contextp->context, &ent); goto fail; } krb5_unparse_name_fixed(contextp->context, ent.principal, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, ent.principal); if(ret){ kadm5_free_principal_ent(contextp->context, &ent); memset(password, 0, strlen(password)); free(password); goto fail; } ret = kadm5_create_principal(kadm_handlep, &ent, mask, password); kadm5_free_principal_ent(kadm_handlep, &ent); memset(password, 0, strlen(password)); free(password); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_modify:{ op = "MODIFY"; ret = kadm5_ret_principal_ent(sp, &ent); if(ret) goto fail; ret = krb5_ret_int32(sp, &mask); if(ret){ kadm5_free_principal_ent(contextp, &ent); goto fail; } krb5_unparse_name_fixed(contextp->context, ent.principal, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY, ent.principal); if(ret){ kadm5_free_principal_ent(contextp, &ent); goto fail; } ret = kadm5_modify_principal(kadm_handlep, &ent, mask); kadm5_free_principal_ent(kadm_handlep, &ent); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_rename:{ op = "RENAME"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; ret = krb5_ret_principal(sp, &princ2); if(ret){ krb5_free_principal(contextp->context, princ); goto fail; } krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_unparse_name_fixed(contextp->context, princ2, name2, sizeof(name2)); krb5_warnx(contextp->context, "%s: %s %s -> %s", client, op, name, name2); ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, princ2) || _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ); if(ret){ krb5_free_principal(contextp->context, princ); krb5_free_principal(contextp->context, princ2); goto fail; } ret = kadm5_rename_principal(kadm_handlep, princ, princ2); krb5_free_principal(contextp->context, princ); krb5_free_principal(contextp->context, princ2); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_chpass:{ op = "CHPASS"; ret = krb5_ret_principal(sp, &princ); if (ret) goto fail; ret = krb5_ret_string(sp, &password); if (ret) { krb5_free_principal(contextp->context, princ); goto fail; } ret = krb5_ret_int32(sp, &keepold); if (ret && ret != HEIM_ERR_EOF) { krb5_free_principal(contextp->context, princ); goto fail; } krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); /* * The change is allowed if at least one of: * * a) allowed by sysadmin * b) it's for the principal him/herself and this was an * initial ticket, but then, check with the password quality * function. * c) the user is on the CPW ACL. */ if (krb5_config_get_bool_default(contextp->context, NULL, TRUE, "kadmin", "allow_self_change_password", NULL) && initial && krb5_principal_compare (contextp->context, contextp->caller, princ)) { krb5_data pwd_data; const char *pwd_reason; pwd_data.data = password; pwd_data.length = strlen(password); pwd_reason = kadm5_check_password_quality (contextp->context, princ, &pwd_data); if (pwd_reason != NULL) ret = KADM5_PASS_Q_DICT; else ret = 0; } else ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); if(ret) { krb5_free_principal(contextp->context, princ); memset(password, 0, strlen(password)); free(password); goto fail; } ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL, password); krb5_free_principal(contextp->context, princ); memset(password, 0, strlen(password)); free(password); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_chpass_with_key:{ int i; krb5_key_data *key_data; int n_key_data; op = "CHPASS_WITH_KEY"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; ret = krb5_ret_int32(sp, &n_key_data); if (ret) { krb5_free_principal(contextp->context, princ); goto fail; } ret = krb5_ret_int32(sp, &keepold); if (ret && ret != HEIM_ERR_EOF) { krb5_free_principal(contextp->context, princ); goto fail; } /* n_key_data will be squeezed into an int16_t below. */ if (n_key_data < 0 || n_key_data >= 1 << 16 || (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) { ret = ERANGE; krb5_free_principal(contextp->context, princ); goto fail; } key_data = malloc (n_key_data * sizeof(*key_data)); if (key_data == NULL && n_key_data != 0) { ret = ENOMEM; krb5_free_principal(contextp->context, princ); goto fail; } for (i = 0; i < n_key_data; ++i) { ret = kadm5_ret_key_data (sp, &key_data[i]); if (ret) { int16_t dummy = i; kadm5_free_key_data (contextp, &dummy, key_data); free (key_data); krb5_free_principal(contextp->context, princ); goto fail; } } krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); /* * The change is only allowed if the user is on the CPW ACL, * this it to force password quality check on the user. */ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); if(ret) { int16_t dummy = n_key_data; kadm5_free_key_data (contextp, &dummy, key_data); free (key_data); krb5_free_principal(contextp->context, princ); goto fail; } ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold, n_key_data, key_data); { int16_t dummy = n_key_data; kadm5_free_key_data (contextp, &dummy, key_data); } free (key_data); krb5_free_principal(contextp->context, princ); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_randkey:{ op = "RANDKEY"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name)); krb5_warnx(contextp->context, "%s: %s %s", client, op, name); /* * The change is allowed if at least one of: * a) it's for the principal him/herself and this was an initial ticket * b) the user is on the CPW ACL. */ if (initial && krb5_principal_compare (contextp->context, contextp->caller, princ)) ret = 0; else ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ); if(ret) { krb5_free_principal(contextp->context, princ); goto fail; } /* * See comments in kadm5_c_randkey_principal() regarding the * protocol. */ ret = krb5_ret_int32(sp, &keepold); if (ret != 0 && ret != HEIM_ERR_EOF) { krb5_free_principal(contextp->context, princ); goto fail; } ret = krb5_ret_int32(sp, &n_ks_tuple); if (ret != 0 && ret != HEIM_ERR_EOF) { krb5_free_principal(contextp->context, princ); goto fail; } else if (ret == 0) { size_t i; if (n_ks_tuple < 0) { ret = EOVERFLOW; krb5_free_principal(contextp->context, princ); goto fail; } if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) { ret = errno; krb5_free_principal(contextp->context, princ); goto fail; } for (i = 0; i < n_ks_tuple; i++) { ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype); if (ret != 0) { krb5_free_principal(contextp->context, princ); goto fail; } ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype); if (ret != 0) { krb5_free_principal(contextp->context, princ); goto fail; } } } ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold, n_ks_tuple, ks_tuple, &new_keys, &n_keys); krb5_free_principal(contextp->context, princ); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if(ret == 0){ int i; krb5_store_int32(sp, n_keys); for(i = 0; i < n_keys; i++){ krb5_store_keyblock(sp, new_keys[i]); krb5_free_keyblock_contents(contextp->context, &new_keys[i]); } free(new_keys); } break; } case kadm_get_privs:{ uint32_t privs; ret = kadm5_get_privs(kadm_handlep, &privs); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if(ret == 0) krb5_store_uint32(sp, privs); break; } case kadm_get_princs:{ op = "LIST"; ret = krb5_ret_int32(sp, &tmp); if(ret) goto fail; if(tmp){ ret = krb5_ret_string(sp, &expression); if(ret) goto fail; }else expression = NULL; krb5_warnx(contextp->context, "%s: %s %s", client, op, expression ? expression : "*"); ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL); if(ret){ free(expression); goto fail; } ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs); free(expression); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if(ret == 0){ int i; krb5_store_int32(sp, n_princs); for(i = 0; i < n_princs; i++) krb5_store_string(sp, princs[i]); kadm5_free_name_list(kadm_handlep, princs, &n_princs); } break; } default: krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, KADM5_FAILURE); break; } krb5_storage_to_data(sp, out); krb5_storage_free(sp); return 0; fail: krb5_warn(contextp->context, ret, "%s", op); krb5_storage_seek(sp, 0, SEEK_SET); krb5_store_int32(sp, ret); krb5_storage_to_data(sp, out); krb5_storage_free(sp); return 0; }
static kadm5_ret_t kadmind_dispatch(void *kadm_handle, krb5_boolean initial, krb5_data *in, krb5_data *out) { kadm5_ret_t ret; int32_t cmd, mask, tmp; kadm5_server_context *context = kadm_handle; char client[128], name[128], name2[128]; char *op = ""; krb5_principal princ, princ2; kadm5_principal_ent_rec ent; char *password, *expression; krb5_keyblock *new_keys; int n_keys; char **princs; int n_princs; krb5_storage *sp; krb5_unparse_name_fixed(context->context, context->caller, client, sizeof(client)); sp = krb5_storage_from_data(in); if (sp == NULL) krb5_errx(context->context, 1, "out of memory"); krb5_ret_int32(sp, &cmd); switch(cmd){ case kadm_get:{ op = "GET"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; ret = krb5_ret_int32(sp, &mask); if(ret){ krb5_free_principal(context->context, princ); goto fail; } mask |= KADM5_PRINCIPAL; krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); krb5_warnx(context->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(context, KADM5_PRIV_GET, princ); if(ret){ krb5_free_principal(context->context, princ); goto fail; } ret = kadm5_get_principal(kadm_handle, princ, &ent, mask); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if(ret == 0){ kadm5_store_principal_ent(sp, &ent); kadm5_free_principal_ent(kadm_handle, &ent); } krb5_free_principal(context->context, princ); break; } case kadm_delete:{ op = "DELETE"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); krb5_warnx(context->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE, princ); if(ret){ krb5_free_principal(context->context, princ); goto fail; } ret = kadm5_delete_principal(kadm_handle, princ); krb5_free_principal(context->context, princ); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_create:{ op = "CREATE"; ret = kadm5_ret_principal_ent(sp, &ent); if(ret) goto fail; ret = krb5_ret_int32(sp, &mask); if(ret){ kadm5_free_principal_ent(context->context, &ent); goto fail; } ret = krb5_ret_string(sp, &password); if(ret){ kadm5_free_principal_ent(context->context, &ent); goto fail; } krb5_unparse_name_fixed(context->context, ent.principal, name, sizeof(name)); krb5_warnx(context->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD, ent.principal); if(ret){ kadm5_free_principal_ent(context->context, &ent); memset(password, 0, strlen(password)); free(password); goto fail; } ret = kadm5_create_principal(kadm_handle, &ent, mask, password); kadm5_free_principal_ent(kadm_handle, &ent); memset(password, 0, strlen(password)); free(password); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_modify:{ op = "MODIFY"; ret = kadm5_ret_principal_ent(sp, &ent); if(ret) goto fail; ret = krb5_ret_int32(sp, &mask); if(ret){ kadm5_free_principal_ent(context, &ent); goto fail; } krb5_unparse_name_fixed(context->context, ent.principal, name, sizeof(name)); krb5_warnx(context->context, "%s: %s %s", client, op, name); ret = _kadm5_acl_check_permission(context, KADM5_PRIV_MODIFY, ent.principal); if(ret){ kadm5_free_principal_ent(context, &ent); goto fail; } ret = kadm5_modify_principal(kadm_handle, &ent, mask); kadm5_free_principal_ent(kadm_handle, &ent); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_rename:{ op = "RENAME"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; ret = krb5_ret_principal(sp, &princ2); if(ret){ krb5_free_principal(context->context, princ); goto fail; } krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); krb5_unparse_name_fixed(context->context, princ2, name2, sizeof(name2)); krb5_warnx(context->context, "%s: %s %s -> %s", client, op, name, name2); ret = _kadm5_acl_check_permission(context, KADM5_PRIV_ADD, princ2) || _kadm5_acl_check_permission(context, KADM5_PRIV_DELETE, princ); if(ret){ krb5_free_principal(context->context, princ); krb5_free_principal(context->context, princ2); goto fail; } ret = kadm5_rename_principal(kadm_handle, princ, princ2); krb5_free_principal(context->context, princ); krb5_free_principal(context->context, princ2); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_chpass:{ op = "CHPASS"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; ret = krb5_ret_string(sp, &password); if(ret){ krb5_free_principal(context->context, princ); goto fail; } krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); krb5_warnx(context->context, "%s: %s %s", client, op, name); /* * The change is allowed if at least one of: * a) it's for the principal him/herself and this was an * initial ticket, but then, check with the password quality * function. * b) the user is on the CPW ACL. */ if (initial && krb5_principal_compare (context->context, context->caller, princ)) { krb5_data pwd_data; const char *pwd_reason; pwd_data.data = password; pwd_data.length = strlen(password); pwd_reason = kadm5_check_password_quality (context->context, princ, &pwd_data); if (pwd_reason != NULL) ret = KADM5_PASS_Q_DICT; else ret = 0; } else ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ); if(ret) { krb5_free_principal(context->context, princ); memset(password, 0, strlen(password)); free(password); goto fail; } ret = kadm5_chpass_principal(kadm_handle, princ, password); krb5_free_principal(context->context, princ); memset(password, 0, strlen(password)); free(password); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_chpass_with_key:{ int i; krb5_key_data *key_data; int n_key_data; op = "CHPASS_WITH_KEY"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; ret = krb5_ret_int32(sp, &n_key_data); if (ret) { krb5_free_principal(context->context, princ); goto fail; } /* n_key_data will be squeezed into an int16_t below. */ if (n_key_data < 0 || n_key_data >= 1 << 16 || n_key_data > UINT_MAX/sizeof(*key_data)) { ret = ERANGE; krb5_free_principal(context->context, princ); goto fail; } key_data = malloc (n_key_data * sizeof(*key_data)); if (key_data == NULL && n_key_data != 0) { ret = ENOMEM; krb5_free_principal(context->context, princ); goto fail; } for (i = 0; i < n_key_data; ++i) { ret = kadm5_ret_key_data (sp, &key_data[i]); if (ret) { int16_t dummy = i; kadm5_free_key_data (context, &dummy, key_data); free (key_data); krb5_free_principal(context->context, princ); goto fail; } } krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); krb5_warnx(context->context, "%s: %s %s", client, op, name); /* * The change is only allowed if the user is on the CPW ACL, * this it to force password quality check on the user. */ ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ); if(ret) { int16_t dummy = n_key_data; kadm5_free_key_data (context, &dummy, key_data); free (key_data); krb5_free_principal(context->context, princ); goto fail; } ret = kadm5_chpass_principal_with_key(kadm_handle, princ, n_key_data, key_data); { int16_t dummy = n_key_data; kadm5_free_key_data (context, &dummy, key_data); } free (key_data); krb5_free_principal(context->context, princ); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); break; } case kadm_randkey:{ op = "RANDKEY"; ret = krb5_ret_principal(sp, &princ); if(ret) goto fail; krb5_unparse_name_fixed(context->context, princ, name, sizeof(name)); krb5_warnx(context->context, "%s: %s %s", client, op, name); /* * The change is allowed if at least one of: * a) it's for the principal him/herself and this was an initial ticket * b) the user is on the CPW ACL. */ if (initial && krb5_principal_compare (context->context, context->caller, princ)) ret = 0; else ret = _kadm5_acl_check_permission(context, KADM5_PRIV_CPW, princ); if(ret) { krb5_free_principal(context->context, princ); goto fail; } ret = kadm5_randkey_principal(kadm_handle, princ, &new_keys, &n_keys); krb5_free_principal(context->context, princ); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if(ret == 0){ int i; krb5_store_int32(sp, n_keys); for(i = 0; i < n_keys; i++){ krb5_store_keyblock(sp, new_keys[i]); krb5_free_keyblock_contents(context->context, &new_keys[i]); } free(new_keys); } break; } case kadm_get_privs:{ uint32_t privs; ret = kadm5_get_privs(kadm_handle, &privs); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if(ret == 0) krb5_store_uint32(sp, privs); break; } case kadm_get_princs:{ op = "LIST"; ret = krb5_ret_int32(sp, &tmp); if(ret) goto fail; if(tmp){ ret = krb5_ret_string(sp, &expression); if(ret) goto fail; }else expression = NULL; krb5_warnx(context->context, "%s: %s %s", client, op, expression ? expression : "*"); ret = _kadm5_acl_check_permission(context, KADM5_PRIV_LIST, NULL); if(ret){ free(expression); goto fail; } ret = kadm5_get_principals(kadm_handle, expression, &princs, &n_princs); free(expression); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, ret); if(ret == 0){ int i; krb5_store_int32(sp, n_princs); for(i = 0; i < n_princs; i++) krb5_store_string(sp, princs[i]); kadm5_free_name_list(kadm_handle, princs, &n_princs); } break; } default: krb5_warnx(context->context, "%s: UNKNOWN OP %d", client, cmd); krb5_storage_free(sp); sp = krb5_storage_emem(); krb5_store_int32(sp, KADM5_FAILURE); break; } krb5_storage_to_data(sp, out); krb5_storage_free(sp); return 0; fail: krb5_warn(context->context, ret, "%s", op); krb5_storage_seek(sp, 0, SEEK_SET); krb5_store_int32(sp, ret); krb5_storage_to_data(sp, out); krb5_storage_free(sp); return 0; }
kadm5_ret_t kadm5_c_randkey_principal(void *server_handle, krb5_principal princ, krb5_boolean keepold, int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, krb5_keyblock **new_keys, int *n_keys) { kadm5_client_context *context = server_handle; kadm5_ret_t ret; krb5_storage *sp; unsigned char buf[1536]; int32_t tmp; size_t i; krb5_data reply; ret = _kadm5_connect(server_handle); if(ret) return ret; sp = krb5_storage_from_mem(buf, sizeof(buf)); if (sp == NULL) { krb5_clear_error_message(context->context); return ENOMEM; } /* * NOTE WELL: This message is extensible. It currently consists of: * * - opcode (kadm_randkey) * - principal name (princ) * * followed by optional items, each of which must be present if * there are any items following them that are also present: * * - keepold boolean (whether to delete old kvnos) * - number of key/salt type tuples * - array of {enctype, salttype} * * Eventually we may add: * * - opaque string2key parameters (salt, rounds, ...) */ krb5_store_int32(sp, kadm_randkey); krb5_store_principal(sp, princ); if (keepold == TRUE || n_ks_tuple > 0) krb5_store_uint32(sp, keepold); if (n_ks_tuple > 0) krb5_store_uint32(sp, n_ks_tuple); for (i = 0; i < n_ks_tuple; i++) { krb5_store_int32(sp, ks_tuple[i].ks_enctype); krb5_store_int32(sp, ks_tuple[i].ks_salttype); } /* Future extensions go here */ ret = _kadm5_client_send(context, sp); krb5_storage_free(sp); if (ret) return ret; ret = _kadm5_client_recv(context, &reply); if(ret) return ret; sp = krb5_storage_from_data(&reply); if (sp == NULL) { krb5_clear_error_message(context->context); krb5_data_free (&reply); return ENOMEM; } krb5_clear_error_message(context->context); krb5_ret_int32(sp, &tmp); ret = tmp; if(ret == 0){ krb5_keyblock *k; krb5_ret_int32(sp, &tmp); if (tmp < 0) { ret = EOVERFLOW; goto out; } k = malloc(tmp * sizeof(*k)); if (k == NULL) { ret = ENOMEM; goto out; } for(i = 0; i < tmp; i++) krb5_ret_keyblock(sp, &k[i]); if (n_keys && new_keys) { *n_keys = tmp; *new_keys = k; } } out: krb5_storage_free(sp); krb5_data_free (&reply); return ret; }
static krb5_error_code mdb_value2entry(krb5_context context, krb5_data *data, hdb_entry *entry) { krb5_error_code ret; krb5_storage *sp; uint32_t u32; uint16_t u16, num_keys, num_tl; size_t i, j; char *p = NULL; sp = krb5_storage_from_data(data); if (sp == NULL) { krb5_set_error_message(context, ENOMEM, "out of memory"); return ENOMEM; } krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); /* 16: baselength */ CHECK(ret = krb5_ret_uint16(sp, &u16)); if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; } /* 32: attributes */ CHECK(ret = krb5_ret_uint32(sp, &u32)); entry->flags.postdate = !(u16 & KRB5_KDB_DISALLOW_POSTDATED); entry->flags.forwardable = !(u16 & KRB5_KDB_DISALLOW_FORWARDABLE); entry->flags.initial = !!(u16 & KRB5_KDB_DISALLOW_TGT_BASED); entry->flags.renewable = !(u16 & KRB5_KDB_DISALLOW_RENEWABLE); entry->flags.proxiable = !(u16 & KRB5_KDB_DISALLOW_PROXIABLE); /* DUP_SKEY */ entry->flags.invalid = !!(u16 & KRB5_KDB_DISALLOW_ALL_TIX); entry->flags.require_preauth =!!(u16 & KRB5_KDB_REQUIRES_PRE_AUTH); entry->flags.require_hwauth =!!(u16 & KRB5_KDB_REQUIRES_HW_AUTH); entry->flags.server = !(u16 & KRB5_KDB_DISALLOW_SVR); entry->flags.change_pw = !!(u16 & KRB5_KDB_PWCHANGE_SERVICE); entry->flags.client = 1; /* XXX */ /* 32: max time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_life = malloc(sizeof(*entry->max_life)); *entry->max_life = u32; } /* 32: max renewable time */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->max_renew = malloc(sizeof(*entry->max_renew)); *entry->max_renew = u32; } /* 32: client expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->valid_end = malloc(sizeof(*entry->valid_end)); *entry->valid_end = u32; } /* 32: passwd expire */ CHECK(ret = krb5_ret_uint32(sp, &u32)); if (u32) { entry->pw_end = malloc(sizeof(*entry->pw_end)); *entry->pw_end = u32; } /* 32: last successful passwd */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: last failed attempt */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 32: num of failed attempts */ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* 16: num tl data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_tl = u16; /* 16: num key data */ CHECK(ret = krb5_ret_uint16(sp, &u16)); num_keys = u16; /* 16: principal length */ CHECK(ret = krb5_ret_uint16(sp, &u16)); /* length: principal */ { p = malloc(u16 + 1); krb5_storage_read(sp, p, u16); p[u16] = '\0'; CHECK(ret = krb5_parse_name(context, p, &entry->principal)); free(p); p = NULL; } /* for num tl data times 16: tl data type 16: tl data length length: length */ for (i = 0; i < num_tl; i++) { CHECK(ret = krb5_ret_uint16(sp, &u16)); CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, SEEK_CUR); } /* for num key data times 16: version (num keyblocks) 16: kvno for version times: 16: type 16: length length: keydata */ for (i = 0; i < num_keys; i++) { int keep = 0; uint16_t version; void *ptr; CHECK(ret = krb5_ret_uint16(sp, &u16)); version = u16; CHECK(ret = krb5_ret_uint16(sp, &u16)); if (entry->kvno < u16) { keep = 1; entry->kvno = u16; for (j = 0; j < entry->keys.len; j++) { free_Key(&entry->keys.val[j]); free(entry->keys.val); entry->keys.len = 0; entry->keys.val = NULL; } } else if (entry->kvno == u16) keep = 1; if (keep) { Key *k; ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1)); if (ptr == NULL) { ret = ENOMEM; goto out; } entry->keys.val = ptr; /* k points to current Key */ k = &entry->keys.val[entry->keys.len]; memset(k, 0, sizeof(*k)); entry->keys.len += 1; entry->keys.val[i].mkvno = malloc(sizeof(*entry->keys.val[i].mkvno)); if (entry->keys.val[i].mkvno == NULL) { ret = ENOMEM; goto out; } *entry->keys.val[i].mkvno = 1; for (j = 0; j < version; j++) { uint16_t type; CHECK(ret = krb5_ret_uint16(sp, &type)); CHECK(ret = krb5_ret_uint16(sp, &u16)); if (j == 0) { /* key */ k->key.keytype = type; if (u16 < 2) { ret = EINVAL; goto out; } krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */ k->key.keyvalue.length = u16 - 2; k->key.keyvalue.data = malloc(k->key.keyvalue.length); krb5_storage_read(sp, k->key.keyvalue.data, k->key.keyvalue.length); } else if (j == 1) { /* salt */ k->salt = calloc(1, sizeof(*k->salt)); if (k->salt == NULL) { ret = ENOMEM; goto out; } k->salt->type = type; if (u16 != 0) { k->salt->salt.data = malloc(u16); if (k->salt->salt.data == NULL) { ret = ENOMEM; goto out; } k->salt->salt.length = u16; krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length); } fix_salt(context, entry, entry->keys.len - 1); } else { krb5_storage_seek(sp, u16, SEEK_CUR); } } } else { /* skip */ for (j = 0; j < version; j++) { CHECK(ret = krb5_ret_uint16(sp, &u16)); CHECK(ret = krb5_ret_uint16(sp, &u16)); krb5_storage_seek(sp, u16, u16); } } } return 0; out: if (p) free(p); free_hdb_entry(entry); return ret; }
static int send_diffs (krb5_context context, slave *s, int log_fd, const char *database, uint32_t current_version) { krb5_storage *sp; uint32_t ver; time_t timestamp; enum kadm_ops op; uint32_t len; off_t right, left; krb5_data data; int ret = 0; if (s->version == current_version) { krb5_warnx(context, "slave %s in sync already at version %ld", s->name, (long)s->version); return 0; } if (s->flags & SLAVE_F_DEAD) return 0; /* if slave is a fresh client, starting over */ if (s->version == 0) { krb5_warnx(context, "sending complete log to fresh slave %s", s->name); return send_complete (context, s, database, current_version); } sp = kadm5_log_goto_end (log_fd); right = krb5_storage_seek(sp, 0, SEEK_CUR); for (;;) { ret = kadm5_log_previous (context, sp, &ver, ×tamp, &op, &len); if (ret) krb5_err(context, 1, ret, "send_diffs: failed to find previous entry"); left = krb5_storage_seek(sp, -16, SEEK_CUR); if (ver == s->version) return 0; if (ver == s->version + 1) break; if (left == 0) { krb5_storage_free(sp); krb5_warnx(context, "slave %s (version %lu) out of sync with master " "(first version in log %lu), sending complete database", s->name, (unsigned long)s->version, (unsigned long)ver); return send_complete (context, s, database, current_version); } } krb5_warnx(context, "syncing slave %s from version %lu to version %lu", s->name, (unsigned long)s->version, (unsigned long)current_version); ret = krb5_data_alloc (&data, right - left + 4); if (ret) { krb5_storage_free(sp); krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); slave_dead(context, s); return 1; } krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); krb5_storage_free(sp); sp = krb5_storage_from_data (&data); if (sp == NULL) { krb5_warnx (context, "send_diffs: krb5_storage_from_data"); slave_dead(context, s); return 1; } krb5_store_int32 (sp, FOR_YOU); krb5_storage_free(sp); ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); krb5_data_free(&data); if (ret) { krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); slave_dead(context, s); return 1; } slave_seen(s); s->version = current_version; return 0; }