static int send_and_recv_tcp(krb5_socket_t fd, time_t tmout, const krb5_data *req, krb5_data *rep) { unsigned char len[4]; unsigned long rep_len; krb5_data len_data; _krb5_put_int(len, req->length, 4); if(net_write (fd, len, sizeof(len)) < 0) return -1; if(net_write (fd, req->data, req->length) < 0) return -1; if (recv_loop (fd, tmout, 0, 4, &len_data) < 0) return -1; if (len_data.length != 4) { krb5_data_free (&len_data); return -1; } _krb5_get_int(len_data.data, &rep_len, 4); krb5_data_free (&len_data); if (recv_loop (fd, tmout, 0, rep_len, rep) < 0) return -1; if(rep->length != rep_len) { krb5_data_free (rep); return -1; } return 0; }
static int send_and_recv_http(krb5_socket_t fd, time_t tmout, const char *prefix, const krb5_data *req, krb5_data *rep) { char *request = NULL; char *str; int ret; int len = base64_encode(req->data, req->length, &str); if(len < 0) return -1; ret = asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str); free(str); if (ret < 0 || request == NULL) return -1; ret = net_write (fd, request, strlen(request)); free (request); if (ret < 0) return ret; ret = recv_loop(fd, tmout, 0, 0, rep); if(ret) return ret; { unsigned long rep_len; char *s, *p; s = realloc(rep->data, rep->length + 1); if (s == NULL) { krb5_data_free (rep); return -1; } s[rep->length] = 0; p = strstr(s, "\r\n\r\n"); if(p == NULL) { krb5_data_zero(rep); free(s); return -1; } p += 4; rep->data = s; rep->length -= p - s; if(rep->length < 4) { /* remove length */ krb5_data_zero(rep); free(s); return -1; } rep->length -= 4; _krb5_get_int(p, &rep_len, 4); if (rep_len != rep->length) { krb5_data_zero(rep); free(s); return -1; } memmove(rep->data, p + 4, rep->length); } return 0; }
static int ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr, unsigned long len, krb5_address *low, krb5_address *high) { unsigned long ia; uint32_t l, h, m = 0xffffffff; if (len > 32) { krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, N_("IPv4 prefix too large (%ld)", "len"), len); return KRB5_PROG_ATYPE_NOSUPP; } m = m << (32 - len); _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length); l = ia & m; h = l | ~m; low->addr_type = KRB5_ADDRESS_INET; if(krb5_data_alloc(&low->address, 4) != 0) return -1; _krb5_put_int(low->address.data, l, low->address.length); high->addr_type = KRB5_ADDRESS_INET; if(krb5_data_alloc(&high->address, 4) != 0) { krb5_free_address(context, low); return -1; } _krb5_put_int(high->address.data, h, high->address.length); return 0; }
ssize_t do_read (int fd, void *buf, size_t sz, void *ivec) { if (do_encrypt) { #ifdef KRB5 if(auth_method == AUTH_KRB5) { krb5_error_code ret; uint32_t len, outer_len; int status; krb5_data data; void *edata; ret = krb5_net_read (context, &fd, &len, 4); if (ret <= 0) return ret; len = ntohl(len); if (len > sz) abort (); /* ivec will be non null for protocol version 2 */ if(ivec != NULL) outer_len = krb5_get_wrapped_length (context, crypto, len + 4); else outer_len = krb5_get_wrapped_length (context, crypto, len); edata = malloc (outer_len); if (edata == NULL) errx (1, "malloc: cannot allocate %u bytes", outer_len); ret = krb5_net_read (context, &fd, edata, outer_len); if (ret <= 0) { free(edata); return ret; } status = krb5_decrypt_ivec(context, crypto, key_usage, edata, outer_len, &data, ivec); free (edata); if (status) krb5_err (context, 1, status, "decrypting data"); if(ivec != NULL) { unsigned long l; if(data.length < len + 4) errx (1, "data received is too short"); _krb5_get_int(data.data, &l, 4); if(l != len) errx (1, "inconsistency in received data"); memcpy (buf, (unsigned char *)data.data+4, len); } else memcpy (buf, data.data, len); krb5_data_free (&data); return len; } else #endif /* KRB5 */ abort (); } else return read (fd, buf, sz); }
static krb5_error_code AES_SHA1_string_to_key(krb5_context context, krb5_enctype enctype, krb5_data password, krb5_salt salt, krb5_data opaque, krb5_keyblock *key) { krb5_error_code ret; uint32_t iter; struct _krb5_encryption_type *et; struct _krb5_key_data kd; if (opaque.length == 0) iter = _krb5_AES_SHA1_string_to_default_iterator; else if (opaque.length == 4) { unsigned long v; _krb5_get_int(opaque.data, &v, 4); iter = ((uint32_t)v); } else return KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */ et = _krb5_find_enctype(enctype); if (et == NULL) return KRB5_PROG_KEYTYPE_NOSUPP; kd.schedule = NULL; ALLOC(kd.key, 1); if (kd.key == NULL) return krb5_enomem(context); kd.key->keytype = enctype; ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size); if (ret) { krb5_set_error_message (context, ret, N_("malloc: out of memory", "")); return ret; } ret = PKCS5_PBKDF2_HMAC(password.data, password.length, salt.saltvalue.data, salt.saltvalue.length, iter, EVP_sha1(), et->keytype->size, kd.key->keyvalue.data); if (ret != 1) { _krb5_free_key_data(context, &kd, et); krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, "Error calculating s2k"); return KRB5_PROG_KEYTYPE_NOSUPP; } ret = _krb5_derive_key(context, et, &kd, "kerberos", strlen("kerberos")); if (ret == 0) ret = krb5_copy_keyblock_contents(context, kd.key, key); _krb5_free_key_data(context, &kd, et); return ret; }
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; }
static krb5_error_code krb5_ret_int(krb5_storage *sp, int32_t *value, size_t len) { int ret; unsigned char v[4]; unsigned long w; ret = sp->fetch(sp, v, len); if(ret != len) return (ret<0)?errno:sp->eof_code; _krb5_get_int(v, &w, len); *value = w; return 0; }
krb5_error_code kadmind_loop(krb5_context context, krb5_keytab keytab, krb5_socket_t sock) { u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4]; ssize_t n; unsigned long len; n = krb5_net_read(context, &sock, buf, 4); if(n == 0) exit(0); if(n < 0) krb5_err(context, 1, errno, "read"); _krb5_get_int(buf, &len, 4); if (len == sizeof(KRB5_SENDAUTH_VERSION)) { n = krb5_net_read(context, &sock, buf + 4, len); if (n < 0) krb5_err (context, 1, errno, "reading sendauth version"); if (n == 0) krb5_errx (context, 1, "EOF reading sendauth version"); if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) { handle_v5(context, keytab, sock); return 0; } len += 4; } else len = 4; handle_mit(context, buf, len, sock); return 0; }
static krb5_error_code process_reply (krb5_context context, krb5_auth_context auth_context, int is_stream, int sock, int *result_code, krb5_data *result_code_string, krb5_data *result_string, const char *host) { krb5_error_code ret; u_char reply[1024 * 3]; ssize_t len; u_int16_t pkt_len, pkt_ver; krb5_data ap_rep_data; int save_errno; len = 0; if (is_stream) { while (len < sizeof(reply)) { unsigned long size; ret = recvfrom (sock, reply + len, sizeof(reply) - len, 0, NULL, NULL); if (ret < 0) { save_errno = errno; krb5_set_error_string(context, "recvfrom %s: %s", host, strerror(save_errno)); return save_errno; } else if (ret == 0) { krb5_set_error_string(context, "recvfrom timeout %s", host); return 1; } len += ret; if (len < 4) continue; _krb5_get_int(reply, &size, 4); if (size + 4 < len) continue; memmove(reply, reply + 4, size); len = size; break; } if (len == sizeof(reply)) { krb5_set_error_string(context, "message too large from %s", host); return ENOMEM; } } else { ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL); if (ret < 0) { save_errno = errno; krb5_set_error_string(context, "recvfrom %s: %s", host, strerror(save_errno)); return save_errno; } len = ret; } if (len < 6) { str2data (result_string, "server %s sent to too short message " "(%ld bytes)", host, (long)len); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } pkt_len = (reply[0] << 8) | (reply[1]); pkt_ver = (reply[2] << 8) | (reply[3]); if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) { KRB_ERROR error; size_t size; u_char *p; memset(&error, 0, sizeof(error)); ret = decode_KRB_ERROR(reply, len, &error, &size); if (ret) return ret; if (error.e_data->length < 2) { str2data(result_string, "server %s sent too short " "e_data to print anything usable", host); free_KRB_ERROR(&error); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } p = error.e_data->data; *result_code = (p[0] << 8) | p[1]; if (error.e_data->length == 2) str2data(result_string, "server only sent error code"); else krb5_data_copy (result_string, p + 2, error.e_data->length - 2); free_KRB_ERROR(&error); return 0; } if (pkt_len != len) { str2data (result_string, "client: wrong len in reply"); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) { str2data (result_string, "client: wrong version number (%d)", pkt_ver); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } ap_rep_data.data = reply + 6; ap_rep_data.length = (reply[4] << 8) | (reply[5]); if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) { str2data (result_string, "client: wrong AP len in reply"); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } if (ap_rep_data.length) { krb5_ap_rep_enc_part *ap_rep; krb5_data priv_data; u_char *p; priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length; priv_data.length = len - ap_rep_data.length - 6; ret = krb5_rd_rep (context, auth_context, &ap_rep_data, &ap_rep); if (ret) return ret; krb5_free_ap_rep_enc_part (context, ap_rep); ret = krb5_rd_priv (context, auth_context, &priv_data, result_code_string, NULL); if (ret) { krb5_data_free (result_code_string); return ret; } if (result_code_string->length < 2) { *result_code = KRB5_KPASSWD_MALFORMED; str2data (result_string, "client: bad length in result"); return 0; } p = result_code_string->data; *result_code = (p[0] << 8) | p[1]; krb5_data_copy (result_string, (unsigned char*)result_code_string->data + 2, result_code_string->length - 2); return 0; } else { KRB_ERROR error; size_t size; u_char *p; ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size); if (ret) { return ret; } if (error.e_data->length < 2) { krb5_warnx (context, "too short e_data to print anything usable"); return 1; /* XXX */ } p = error.e_data->data; *result_code = (p[0] << 8) | p[1]; krb5_data_copy (result_string, p + 2, error.e_data->length - 2); return 0; } }
static krb5_error_code AES_SHA2_string_to_key(krb5_context context, krb5_enctype enctype, krb5_data password, krb5_salt salt, krb5_data opaque, krb5_keyblock *key) { krb5_error_code ret; uint32_t iter; struct _krb5_encryption_type *et = NULL; struct _krb5_key_data kd; krb5_data saltp; size_t enctypesz; const EVP_MD *md = NULL; krb5_data_zero(&saltp); kd.key = NULL; kd.schedule = NULL; if (opaque.length == 0) { iter = _krb5_AES_SHA2_string_to_default_iterator; } else if (opaque.length == 4) { unsigned long v; _krb5_get_int(opaque.data, &v, 4); iter = ((uint32_t)v); } else { ret = KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */ goto cleanup; } et = _krb5_find_enctype(enctype); if (et == NULL) { ret = KRB5_PROG_KEYTYPE_NOSUPP; goto cleanup; } kd.schedule = NULL; ALLOC(kd.key, 1); if (kd.key == NULL) { ret = krb5_enomem(context); goto cleanup; } kd.key->keytype = enctype; ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size); if (ret) { ret = krb5_enomem(context); goto cleanup; } enctypesz = strlen(et->name) + 1; ret = krb5_data_alloc(&saltp, enctypesz + salt.saltvalue.length); if (ret) { ret = krb5_enomem(context); goto cleanup; } memcpy(saltp.data, et->name, enctypesz); memcpy((unsigned char *)saltp.data + enctypesz, salt.saltvalue.data, salt.saltvalue.length); ret = _krb5_aes_sha2_md_for_enctype(context, enctype, &md); if (ret) goto cleanup; ret = PKCS5_PBKDF2_HMAC(password.data, password.length, saltp.data, saltp.length, iter, md, et->keytype->size, kd.key->keyvalue.data); if (ret != 1) { krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, "Error calculating s2k"); ret = KRB5_PROG_KEYTYPE_NOSUPP; goto cleanup; } ret = _krb5_derive_key(context, et, &kd, "kerberos", strlen("kerberos")); if (ret) goto cleanup; ret = krb5_copy_keyblock_contents(context, kd.key, key); if (ret) goto cleanup; cleanup: krb5_data_free(&saltp); _krb5_free_key_data(context, &kd, et); return ret; }