static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out) { struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; krb5_auth_context auth_context = gensec_krb5_state->auth_context; krb5_error_code ret; krb5_data input, output; krb5_replay_data replay; input.length = in->length; input.data = in->data; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { ret = krb5_rd_priv(context, auth_context, &input, &output, &replay); if (ret) { DEBUG(1, ("krb5_rd_priv failed: %s\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, mem_ctx))); return NT_STATUS_ACCESS_DENIED; } *out = data_blob_talloc(mem_ctx, output.data, output.length); krb5_data_free(&output); } else { return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_read_priv_message(krb5_context context, krb5_auth_context ac, krb5_pointer p_fd, krb5_data *data) { krb5_error_code ret; krb5_data packet; ret = krb5_read_message(context, p_fd, &packet); if(ret) return ret; ret = krb5_rd_priv (context, ac, &packet, data, NULL); krb5_data_free(&packet); return ret; }
kadm5_ret_t _kadm5_client_recv(kadm5_client_context *context, krb5_data *reply) { krb5_error_code ret; krb5_data data; krb5_storage *sock; sock = krb5_storage_from_fd(context->sock); if(sock == NULL) return ENOMEM; ret = krb5_ret_data(sock, &data); krb5_storage_free(sock); if(ret == KRB5_CC_END) return KADM5_RPC_ERROR; else if(ret) return ret; ret = krb5_rd_priv(context->context, context->ac, &data, reply, NULL); krb5_data_free(&data); return ret; }
int do_krb5_comm(krb5_context context, krb5_keytab keytab, krb5_principal server, char *cmddir) { struct sockaddr_in c_saddr, s_saddr; socklen_t namelen; int sock = 0; int len; char buff[BUFFSIZE]; char *cname = NULL; krb5_error_code retval; krb5_data kdata, message; krb5_auth_context auth_context = NULL; krb5_ticket *ticket; krb5_address ckaddr, skaddr; krb5_rcache rcache; krb5_data rcache_name; long srand, rrand; int fd[2]; char rcname_piece[RC_PIECE_MAXLEN]; namelen = sizeof(c_saddr); if (getpeername(sock, (struct sockaddr *)&c_saddr, &namelen) < 0) { syslog(LOG_ERR, "getpeername: %m"); return 1; } namelen = sizeof(s_saddr); if (getsockname(sock, (struct sockaddr *)&s_saddr, &namelen) < 0) { syslog(LOG_ERR, "getsockname: %m"); return 1; } /* INIT MSG = random number */ srand = random(); /* Send it */ if (send(sock, &srand, sizeof(srand), 0) < 0) { syslog(LOG_ERR, "%m while sending init message"); return 1; } if (recv(sock, &rrand, sizeof(rrand), 0) < 0) { syslog(LOG_ERR, "%m while receiving init reply"); return 1; } /* Reply should contain the same message (number) */ if (srand != rrand) { syslog(LOG_ERR, "Bad init reply"); return 1; } /* Do authentication */ if (retval = krb5_recvauth(context, &auth_context, (krb5_pointer)&sock, AFSADM_VERSION, server, 0, keytab, &ticket)) { syslog(LOG_ERR, "recvauth failed: %s", error_message(retval)); exit(1); } /* Get client name */ if (retval = krb5_unparse_name(context, ticket->enc_part2->client, &cname)) { syslog(LOG_ERR, "unparse failed: %s", error_message(retval)); return 1; } if (ticket) krb5_free_ticket(context, ticket); if (debug) syslog(LOG_DEBUG, "Principal %s", cname); /*******************************************************************/ ckaddr.addrtype = ADDRTYPE_IPPORT; ckaddr.length = sizeof(c_saddr.sin_port); ckaddr.contents = (krb5_octet *)&c_saddr.sin_port; skaddr.addrtype = ADDRTYPE_IPPORT; skaddr.length = sizeof(s_saddr.sin_port); skaddr.contents = (krb5_octet *)&s_saddr.sin_port; if ((retval = krb5_auth_con_setports(context, auth_context, &skaddr, &ckaddr))) { syslog(LOG_ERR, "%s while setting ports", error_message(retval)); return 1; } /* Set foreign_addr for rd_priv() */ ckaddr.addrtype = ADDRTYPE_INET; ckaddr.length = sizeof(c_saddr.sin_addr); ckaddr.contents = (krb5_octet *)&c_saddr.sin_addr; /* Set local_addr */ skaddr.addrtype = ADDRTYPE_INET; skaddr.length = sizeof(s_saddr.sin_addr); skaddr.contents = (krb5_octet *)&s_saddr.sin_addr; if ((retval = krb5_auth_con_setaddrs(context, auth_context, &skaddr, &ckaddr))) { syslog(LOG_ERR, "%s while setting addrs", error_message(retval)); return 1; } /* Receive a request */ if ((len = recv(sock, (char *)buff, sizeof(buff), 0)) < 0) { syslog(LOG_ERR, "%m while receiving datagram"); return 1; } kdata.length = len; kdata.data = buff; if (debug) syslog(LOG_DEBUG, "Received %d bytes", len); /* Decrypt it */ if ((retval = krb5_rd_priv(context, auth_context, &kdata, &message, NULL))) { syslog(LOG_ERR, "%s while verifying PRIV message", error_message(retval)); return 1; } if (message.length > 0) { #ifdef __osf__ sprintf(rcname_piece, "afsadmd_%d", getpid()); #else snprintf(rcname_piece, RC_PIECE_MAXLEN, "afsadmd_%d", getpid()); #endif rcache_name.data = rcname_piece; rcache_name.length = strlen(rcache_name.data); if ((retval = krb5_get_server_rcache(context, &rcache_name, &rcache))) { syslog(LOG_ERR, "%s while getting server rcache", error_message(retval)); return 1; } /* set auth_context rcache */ if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) { syslog(LOG_ERR, "%s while setting rcache", error_message(retval)); return 1; } /********************************************************************* * Call the desired command, read stdout/stderr, send it *********************************************************************/ /* create fork */ if (pipe(fd) == -1) printf("Failed create fork with pipe().\n"); if (fork() == 0) { close(fd[0]); close(1); close(2); dup2(fd[1], 1); dup2(fd[1], 2); /* Call required command */ do_command(context, keytab, server, cname, message.data, cmddir ); krb5_xfree(message.data); exit(0); } else { /* Read stdout/stderr from pipe, store it to the buffer, encrypt it a send to the client */ krb5_data message, kdata; char buff[PIPEBUFF]; int n = 0; int len = 0; int sent = 0; int counter = 0; int end = 0; short netlen; time_t starttime, oldtime, newtime; FILE *pipedes; close(fd[1]); pipedes = fdopen(fd[0], "r"); starttime = oldtime = time(NULL); for (n = 0; end == 0; ) { /* Read line from pipe */ if (fgets(buff + n, PIPEBUFF - n, pipedes) == NULL) end++; else n = strlen(buff); /* Get time */ newtime = time(NULL); /* Send buffer when * a) buffer is full * b) buffer contains data and * 1) end-of-file encountered (end flag) * 2) buffer sent before 1s */ if ((n == PIPEBUFF) || (((newtime > oldtime) || end ) && (n != 0))) { /* Prepare data for sending */ message.data = buff; message.length = n; kdata.data = NULL; /* Make the encrypted message */ if ((retval = krb5_mk_priv(context, auth_context, &message, &kdata, NULL))) { syslog(LOG_ERR, "%s while making KRB_PRIV message", error_message(retval)); return 1; } /* Convert byte order */ netlen = htons((short)kdata.length); /* Send len of encrypted data */ if ((len = send(sock, (char *)&netlen, sizeof(netlen), 0)) != sizeof(netlen)) { krb5_xfree(kdata.data); syslog(LOG_ERR, "%m while sending len of PRIV message"); return 1; } /* Send it */ if ((len = send(sock, (char *)kdata.data, kdata.length, 0)) != kdata.length) { syslog(LOG_ERR, "%m while sending PRIV message"); krb5_xfree(kdata.data); return 1; } /* Statistics */ sent += len; counter++; /* Timestanmp */ oldtime = newtime; n = 0; krb5_xfree(kdata.data); } } newtime = time(NULL); if (debug) syslog(LOG_DEBUG, "Sent %d bytes in %ds [%d fragment(s)]", sent, (int)(newtime - starttime), counter); } } //FIXME: There is no way to close or destroy rcache declared in krb5 headers //krb5_rc_destroy(context, rcache); /* set auth_context rcache */ if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) { syslog(LOG_ERR, "%s while setting rcache to NULL", error_message(retval)); return 1; } free(cname); krb5_auth_con_free(context, auth_context); 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 parse_setpw_reply(krb5_context context, bool use_tcp, krb5_auth_context auth_context, krb5_data *packet) { krb5_data ap_rep; char *p; int vnum, ret, res_code; krb5_data cipherresult; krb5_data clearresult; krb5_ap_rep_enc_part *ap_rep_enc; krb5_replay_data replay; unsigned int msg_length = packet->length; if (packet->length < (use_tcp ? 8 : 4)) { return KRB5KRB_AP_ERR_MODIFIED; } p = (char *)packet->data; /* ** see if it is an error */ if (krb5_is_krb_error(packet)) { ret = handle_krberror_packet(context, packet); if (ret) { return ret; } } /* tcp... */ if (use_tcp) { msg_length -= 4; if (RIVAL(p, 0) != msg_length) { DEBUG(1,("Bad TCP packet length (%d/%d) from kpasswd server\n", RIVAL(p, 0), msg_length)); return KRB5KRB_AP_ERR_MODIFIED; } p += 4; } if (RSVAL(p, 0) != msg_length) { DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n", RSVAL(p, 0), msg_length)); return KRB5KRB_AP_ERR_MODIFIED; } p += 2; vnum = RSVAL(p, 0); p += 2; /* FIXME: According to standard there is only one type of reply */ if (vnum != KRB5_KPASSWD_VERS_SETPW && vnum != KRB5_KPASSWD_VERS_SETPW_ALT && vnum != KRB5_KPASSWD_VERS_CHANGEPW) { DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum)); return KRB5KDC_ERR_BAD_PVNO; } ap_rep.length = RSVAL(p, 0); p += 2; if (p + ap_rep.length >= (char *)packet->data + packet->length) { DEBUG(1,("ptr beyond end of packet from kpasswd server\n")); return KRB5KRB_AP_ERR_MODIFIED; } if (ap_rep.length == 0) { DEBUG(1,("got unencrypted setpw result?!\n")); return KRB5KRB_AP_ERR_MODIFIED; } /* verify ap_rep */ ap_rep.data = p; p += ap_rep.length; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret))); return KRB5KRB_AP_ERR_MODIFIED; } krb5_free_ap_rep_enc_part(context, ap_rep_enc); cipherresult.data = p; cipherresult.length = ((char *)packet->data + packet->length) - p; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, &replay); if (ret) { DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret))); return KRB5KRB_AP_ERR_MODIFIED; } if (clearresult.length < 2) { free(clearresult.data); ret = KRB5KRB_AP_ERR_MODIFIED; return KRB5KRB_AP_ERR_MODIFIED; } p = (char *)clearresult.data; res_code = RSVAL(p, 0); free(clearresult.data); if ((res_code < KRB5_KPASSWD_SUCCESS) || (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) { return KRB5KRB_AP_ERR_MODIFIED; } if (res_code == KRB5_KPASSWD_SUCCESS) { return 0; } else { const char *errstr; setpw_result_code_string(context, res_code, &errstr); DEBUG(1, ("Error changing password: %s (%d)\n", errstr, res_code)); return kpasswd_err_to_krb5_err(res_code); } }
static int proto (int sock, const char *service) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_ticket *ticket; char *name; char hostname[MAXHOSTNAMELEN]; krb5_data packet; krb5_data data; u_int32_t len, net_len; ssize_t n; status = krb5_auth_con_init (context, &auth_context); if (status) krb5_err (context, 1, status, "krb5_auth_con_init"); status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &sock); if (status) krb5_err (context, 1, status, "krb5_auth_con_setaddrs_from_fd"); if(gethostname (hostname, sizeof(hostname)) < 0) krb5_err (context, 1, errno, "gethostname"); status = krb5_sname_to_principal (context, hostname, service, KRB5_NT_SRV_HST, &server); if (status) krb5_err (context, 1, status, "krb5_sname_to_principal"); status = krb5_recvauth (context, &auth_context, &sock, VERSION, server, 0, NULL, &ticket); if (status) krb5_err (context, 1, status, "krb5_recvauth"); status = krb5_unparse_name (context, ticket->client, &name); if (status) krb5_err (context, 1, status, "krb5_unparse_name"); fprintf (stderr, "User is `%s'\n", name); free (name); krb5_data_zero (&data); krb5_data_zero (&packet); n = krb5_net_read (context, &sock, &net_len, 4); if (n == 0) krb5_errx (context, 1, "EOF in krb5_net_read"); if (n < 0) krb5_err (context, 1, errno, "krb5_net_read"); len = ntohl(net_len); krb5_data_alloc (&packet, len); n = krb5_net_read (context, &sock, packet.data, len); if (n == 0) krb5_errx (context, 1, "EOF in krb5_net_read"); if (n < 0) krb5_err (context, 1, errno, "krb5_net_read"); status = krb5_rd_safe (context, auth_context, &packet, &data, NULL); if (status) krb5_err (context, 1, status, "krb5_rd_safe"); fprintf (stderr, "safe packet: %.*s\n", (int)data.length, (char *)data.data); n = krb5_net_read (context, &sock, &net_len, 4); if (n == 0) krb5_errx (context, 1, "EOF in krb5_net_read"); if (n < 0) krb5_err (context, 1, errno, "krb5_net_read"); len = ntohl(net_len); krb5_data_alloc (&packet, len); n = krb5_net_read (context, &sock, packet.data, len); if (n == 0) krb5_errx (context, 1, "EOF in krb5_net_read"); if (n < 0) krb5_err (context, 1, errno, "krb5_net_read"); status = krb5_rd_priv (context, auth_context, &packet, &data, NULL); if (status) krb5_err (context, 1, status, "krb5_rd_priv"); fprintf (stderr, "priv packet: %.*s\n", (int)data.length, (char *)data.data); return 0; }
static int proto (int sock, const char *service) { struct sockaddr_in remote, local; socklen_t addrlen; krb5_address remote_addr, local_addr; krb5_ccache ccache; krb5_auth_context auth_context; krb5_error_code status; krb5_data packet; krb5_data data; krb5_data client_name; krb5_creds in_creds, *out_creds; addrlen = sizeof(local); if (getsockname (sock, (struct sockaddr *)&local, &addrlen) < 0 || addrlen != sizeof(local)) err (1, "getsockname)"); addrlen = sizeof(remote); if (getpeername (sock, (struct sockaddr *)&remote, &addrlen) < 0 || addrlen != sizeof(remote)) err (1, "getpeername"); status = krb5_auth_con_init (context, &auth_context); if (status) errx (1, "krb5_auth_con_init: %s", krb5_get_err_text(context, status)); local_addr.addr_type = AF_INET; local_addr.address.length = sizeof(local.sin_addr); local_addr.address.data = &local.sin_addr; remote_addr.addr_type = AF_INET; remote_addr.address.length = sizeof(remote.sin_addr); remote_addr.address.data = &remote.sin_addr; status = krb5_auth_con_setaddrs (context, auth_context, &local_addr, &remote_addr); if (status) errx (1, "krb5_auth_con_setaddr: %s", krb5_get_err_text(context, status)); status = krb5_read_message(context, &sock, &client_name); if(status) krb5_err(context, 1, status, "krb5_read_message"); memset(&in_creds, 0, sizeof(in_creds)); status = krb5_cc_default(context, &ccache); if(status) krb5_err(context, 1, status, "krb5_cc_default"); status = krb5_cc_get_principal(context, ccache, &in_creds.client); if(status) krb5_err(context, 1, status, "krb5_cc_get_principal"); status = krb5_read_message(context, &sock, &in_creds.second_ticket); if(status) krb5_err(context, 1, status, "krb5_read_message"); status = krb5_parse_name(context, client_name.data, &in_creds.server); if(status) krb5_err(context, 1, status, "krb5_parse_name"); status = krb5_get_credentials(context, KRB5_GC_USER_USER, ccache, &in_creds, &out_creds); if(status) krb5_err(context, 1, status, "krb5_get_credentials"); status = krb5_cc_default(context, &ccache); if(status) krb5_err(context, 1, status, "krb5_cc_default"); status = krb5_sendauth(context, &auth_context, &sock, VERSION, in_creds.client, in_creds.server, AP_OPTS_USE_SESSION_KEY, NULL, out_creds, ccache, NULL, NULL, NULL); if (status) krb5_err(context, 1, status, "krb5_sendauth"); { char *str; krb5_unparse_name(context, in_creds.server, &str); printf ("User is `%s'\n", str); free(str); krb5_unparse_name(context, in_creds.client, &str); printf ("Server is `%s'\n", str); free(str); } krb5_data_zero (&data); krb5_data_zero (&packet); status = krb5_read_message(context, &sock, &packet); if(status) krb5_err(context, 1, status, "krb5_read_message"); status = krb5_rd_safe (context, auth_context, &packet, &data, NULL); if (status) errx (1, "krb5_rd_safe: %s", krb5_get_err_text(context, status)); printf ("safe packet: %.*s\n", (int)data.length, (char *)data.data); status = krb5_read_message(context, &sock, &packet); if(status) krb5_err(context, 1, status, "krb5_read_message"); status = krb5_rd_priv (context, auth_context, &packet, &data, NULL); if (status) errx (1, "krb5_rd_priv: %s", krb5_get_err_text(context, status)); printf ("priv packet: %.*s\n", (int)data.length, (char *)data.data); return 0; }
static krb5_error_code process_reply(krb5_context context, krb5_auth_context auth_context, krb5_data *data, int *result_code, krb5_data *result_code_string, krb5_data *result_string) { krb5_error_code ret; ssize_t len; uint16_t pkt_len, pkt_ver; krb5_data ap_rep_data; uint8_t *reply; krb5_auth_con_clear(context, auth_context, KRB5_AUTH_CONTEXT_CLEAR_LOCAL_ADDR|KRB5_AUTH_CONTEXT_CLEAR_REMOTE_ADDR); len = data->length; reply = data->data;; if (len < 6) { krb5_data_format(result_string, "server sent to too short message " "(%ld bytes)", (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) { krb5_data_format(result_string, "server sent too short " "e_data to print anything usable"); 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) krb5_data_format(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) { krb5_data_format(result_string, "client: wrong len in reply"); *result_code = KRB5_KPASSWD_MALFORMED; return 0; } if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) { krb5_data_format(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) { krb5_data_format(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; krb5_data_format(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 int verify (krb5_auth_context *auth_context, krb5_realm *realms, krb5_keytab keytab, krb5_ticket **ticket, krb5_data *out_data, uint16_t *version, int s, struct sockaddr *sa, int sa_size, u_char *msg, size_t len, krb5_address *client_addr) { krb5_error_code ret; uint16_t pkt_len, pkt_ver, ap_req_len; krb5_data ap_req_data; krb5_data krb_priv_data; krb5_realm *r; /* * Only send an error reply if the request passes basic length * verification. Otherwise, kpasswdd would reply to every UDP packet, * allowing an attacker to set up a ping-pong DoS attack via a spoofed UDP * packet with a source address of another UDP service that also replies * to every packet. * * Also suppress the error reply if ap_req_len is 0, which indicates * either an invalid request or an error packet. An error packet may be * the result of a ping-pong attacker pointing us at another kpasswdd. */ pkt_len = (msg[0] << 8) | (msg[1]); pkt_ver = (msg[2] << 8) | (msg[3]); ap_req_len = (msg[4] << 8) | (msg[5]); if (pkt_len != len) { krb5_warnx (context, "Strange len: %ld != %ld", (long)pkt_len, (long)len); return 1; } if (ap_req_len == 0) { krb5_warnx (context, "Request is error packet (ap_req_len == 0)"); return 1; } if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW && pkt_ver != KRB5_KPASSWD_VERS_SETPW) { krb5_warnx (context, "Bad version (%d)", pkt_ver); reply_error (NULL, s, sa, sa_size, 0, 1, "Wrong program version"); return 1; } *version = pkt_ver; ap_req_data.data = msg + 6; ap_req_data.length = ap_req_len; ret = krb5_rd_req (context, auth_context, &ap_req_data, NULL, keytab, NULL, ticket); if (ret) { krb5_warn (context, ret, "krb5_rd_req"); reply_error (NULL, s, sa, sa_size, ret, 3, "Authentication failed"); return 1; } /* verify realm and principal */ for (r = realms; *r != NULL; r++) { krb5_principal principal; krb5_boolean same; ret = krb5_make_principal (context, &principal, *r, "kadmin", "changepw", NULL); if (ret) krb5_err (context, 1, ret, "krb5_make_principal"); same = krb5_principal_compare(context, principal, (*ticket)->server); krb5_free_principal(context, principal); if (same == TRUE) break; } if (*r == NULL) { char *str; krb5_unparse_name(context, (*ticket)->server, &str); krb5_warnx (context, "client used not valid principal %s", str); free(str); reply_error (NULL, s, sa, sa_size, ret, 1, "Bad request"); goto out; } if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) { krb5_warnx (context, "server realm (%s) not same a client realm (%s)", (*ticket)->server->realm, (*ticket)->client->realm); reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1, "Bad request"); goto out; } if (!(*ticket)->ticket.flags.initial) { krb5_warnx (context, "initial flag not set"); reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1, "Bad request"); goto out; } krb_priv_data.data = msg + 6 + ap_req_len; krb_priv_data.length = len - 6 - ap_req_len; /* * Only enforce client addresses on on tickets with addresses. If * its addressless, we are guessing its behind NAT and really * can't know this information. */ if ((*ticket)->ticket.caddr && (*ticket)->ticket.caddr->len > 0) { ret = krb5_auth_con_setaddrs (context, *auth_context, NULL, client_addr); if (ret) { krb5_warn (context, ret, "krb5_auth_con_setaddr(this)"); goto out; } } ret = krb5_rd_priv (context, *auth_context, &krb_priv_data, out_data, NULL); if (ret) { krb5_warn (context, ret, "krb5_rd_priv"); reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 3, "Bad request"); goto out; } return 0; out: krb5_free_ticket (context, *ticket); ticket = NULL; return 1; }
/* Decode a reply to produce the clear-text output. */ static krb5_error_code get_clear_result(krb5_context context, krb5_auth_context auth_context, const krb5_data *packet, krb5_data **clear_out, krb5_boolean *is_error_out) { krb5_error_code ret; char *ptr, *end = packet->data + packet->length; unsigned int plen, vno, aplen; krb5_data ap_rep, cipher, error; krb5_ap_rep_enc_part *ap_rep_enc; krb5_replay_data replay; krb5_key send_subkey = NULL; krb5_data clear = empty_data(); *clear_out = NULL; *is_error_out = FALSE; /* Check for an unframed KRB-ERROR (expected for RFC 3244 requests; also * received from MS AD for version 1 requests). */ if (krb5_is_krb_error(packet)) { *is_error_out = TRUE; return get_error_edata(context, packet, clear_out); } if (packet->length < 6) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the length. */ ptr = packet->data; plen = (*ptr++ & 0xff); plen = (plen << 8) | (*ptr++ & 0xff); if (plen != packet->length) return KRB5KRB_AP_ERR_MODIFIED; /* Decode and verify the version number. */ vno = (*ptr++ & 0xff); vno = (vno << 8) | (*ptr++ & 0xff); if (vno != 1 && vno != 0xff80) return KRB5KDC_ERR_BAD_PVNO; /* Decode and check the AP-REP length. */ aplen = (*ptr++ & 0xff); aplen = (aplen << 8) | (*ptr++ & 0xff); if (aplen > end - ptr) return KRB5KRB_AP_ERR_MODIFIED; /* A zero-length AP-REQ indicates a framed KRB-ERROR response. (Expected * for protocol version 1; specified but unusual for RFC 3244 requests.) */ if (aplen == 0) { *is_error_out = TRUE; error = make_data(ptr, end - ptr); return get_error_edata(context, &error, clear_out); } /* We have an AP-REP. Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey); if (ret) return ret; /* Verify the AP-REP. */ ap_rep = make_data(ptr, aplen); ptr += ap_rep.length; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) goto cleanup; krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey_k(context, auth_context, send_subkey); if (ret) goto cleanup; /* Extract and decrypt the result. */ cipher = make_data(ptr, end - ptr); ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) goto cleanup; ret = krb5_copy_data(context, &clear, clear_out); if (ret) goto cleanup; *is_error_out = FALSE; cleanup: krb5_k_free_key(context, send_subkey); krb5_free_data_contents(context, &clear); return ret; }
static krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, krb5_keytab keytab, const krb5_fulladdr *local_faddr, const krb5_fulladdr *remote_faddr, krb5_data *req, krb5_data *rep) { krb5_error_code ret; char *ptr; unsigned int plen, vno; krb5_data ap_req, ap_rep = empty_data(); krb5_data cipher = empty_data(), clear = empty_data(); krb5_auth_context auth_context = NULL; krb5_principal changepw = NULL; krb5_principal client, target = NULL; krb5_ticket *ticket = NULL; krb5_replay_data replay; krb5_error krberror; int numresult; char strresult[1024]; char *clientstr = NULL, *targetstr = NULL; const char *errmsg = NULL; size_t clen; char *cdots; struct sockaddr_storage ss; socklen_t salen; char addrbuf[100]; krb5_address *addr = remote_faddr->address; *rep = empty_data(); if (req->length < 4) { /* either this, or the server is printing bad messages, or the caller passed in garbage */ ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated", sizeof(strresult)); goto bailout; } ptr = req->data; /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request length was inconsistent", sizeof(strresult)); goto bailout; } /* verify version number */ vno = (*ptr++ & 0xff) ; vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1 && vno != RFC3244_VERSION) { ret = KRB5KDC_ERR_BAD_PVNO; numresult = KRB5_KPASSWD_BAD_VERSION; snprintf(strresult, sizeof(strresult), "Request contained unknown protocol version number %d", vno); goto bailout; } /* read, check ap-req length */ ap_req.length = (*ptr++ & 0xff); ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); if (ptr + ap_req.length >= req->data + req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated in AP-REQ", sizeof(strresult)); goto bailout; } /* verify ap_req */ ap_req.data = ptr; ptr += ap_req.length; ret = krb5_auth_con_init(context, &auth_context); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_build_principal(context, &changepw, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed building kadmin/changepw principal", sizeof(strresult)); goto chpwfail; } ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, NULL, &ticket); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed reading application request", sizeof(strresult)); goto chpwfail; } /* construct the ap-rep */ ret = krb5_mk_rep(context, auth_context, &ap_rep); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed replying to application request", sizeof(strresult)); goto chpwfail; } /* decrypt the ChangePasswdData */ cipher.length = (req->data + req->length) - ptr; cipher.data = ptr; /* * Don't set a remote address in auth_context before calling krb5_rd_priv, * so that we can work against clients behind a NAT. Reflection attacks * aren't a concern since we use sequence numbers and since our requests * don't look anything like our responses. Also don't set a local address, * since we don't know what interface the request was received on. */ ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed decrypting request", sizeof(strresult)); goto chpwfail; } client = ticket->enc_part2->client; /* decode ChangePasswdData for setpw requests */ if (vno == RFC3244_VERSION) { krb5_data *clear_data; ret = decode_krb5_setpw_req(&clear, &clear_data, &target); if (ret != 0) { numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Failed decoding ChangePasswdData", sizeof(strresult)); goto chpwfail; } zapfree(clear.data, clear.length); clear = *clear_data; free(clear_data); if (target != NULL) { ret = krb5_unparse_name(context, target, &targetstr); if (ret != 0) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing target name for log", sizeof(strresult)); goto chpwfail; } } } ret = krb5_unparse_name(context, client, &clientstr); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing client name for log", sizeof(strresult)); goto chpwfail; } /* for cpw, verify that this is an AS_REQ ticket */ if (vno == 1 && (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) { numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED; strlcpy(strresult, "Ticket must be derived from a password", sizeof(strresult)); goto chpwfail; } /* change the password */ ptr = k5memdup0(clear.data, clear.length, &ret); ret = schpw_util_wrapper(server_handle, client, target, (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0, ptr, NULL, strresult, sizeof(strresult)); if (ret) errmsg = krb5_get_error_message(context, ret); /* zap the password */ zapfree(clear.data, clear.length); zapfree(ptr, clear.length); clear = empty_data(); clen = strlen(clientstr); trunc_name(&clen, &cdots); switch (addr->addrtype) { case ADDRTYPE_INET: { struct sockaddr_in *sin = ss2sin(&ss); sin->sin_family = AF_INET; memcpy(&sin->sin_addr, addr->contents, addr->length); sin->sin_port = htons(remote_faddr->port); salen = sizeof(*sin); break; } case ADDRTYPE_INET6: { struct sockaddr_in6 *sin6 = ss2sin6(&ss); sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, addr->contents, addr->length); sin6->sin6_port = htons(remote_faddr->port); salen = sizeof(*sin6); break; } default: { struct sockaddr *sa = ss2sa(&ss); sa->sa_family = AF_UNSPEC; salen = sizeof(*sa); break; } } if (getnameinfo(ss2sa(&ss), salen, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0) strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf)); if (vno == RFC3244_VERSION) { size_t tlen; char *tdots; const char *targetp; if (target == NULL) { tlen = clen; tdots = cdots; targetp = targetstr; } else { tlen = strlen(targetstr); trunc_name(&tlen, &tdots); targetp = clientstr; } krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for " "%.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, (int) tlen, targetp, tdots, errmsg ? errmsg : "success"); } else { krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, errmsg ? errmsg : "success"); } switch (ret) { case KADM5_AUTH_CHANGEPW: numresult = KRB5_KPASSWD_ACCESSDENIED; break; case KADM5_PASS_Q_TOOSHORT: case KADM5_PASS_REUSE: case KADM5_PASS_Q_CLASS: case KADM5_PASS_Q_DICT: case KADM5_PASS_Q_GENERIC: case KADM5_PASS_TOOSOON: numresult = KRB5_KPASSWD_SOFTERROR; break; case 0: numresult = KRB5_KPASSWD_SUCCESS; strlcpy(strresult, "", sizeof(strresult)); break; default: numresult = KRB5_KPASSWD_HARDERROR; break; } chpwfail: ret = alloc_data(&clear, 2 + strlen(strresult)); if (ret) goto bailout; ptr = clear.data; *ptr++ = (numresult>>8) & 0xff; *ptr++ = numresult & 0xff; memcpy(ptr, strresult, strlen(strresult)); cipher = empty_data(); if (ap_rep.length) { ret = krb5_auth_con_setaddrs(context, auth_context, local_faddr->address, NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed storing client and server internet addresses", sizeof(strresult)); } else { ret = krb5_mk_priv(context, auth_context, &clear, &cipher, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed encrypting reply", sizeof(strresult)); } } } /* if no KRB-PRIV was constructed, then we need a KRB-ERROR. if this fails, just bail. there's nothing else we can do. */ if (cipher.length == 0) { /* clear out ap_rep now, so that it won't be inserted in the reply */ if (ap_rep.length) { free(ap_rep.data); ap_rep = empty_data(); } krberror.ctime = 0; krberror.cusec = 0; krberror.susec = 0; ret = krb5_timeofday(context, &krberror.stime); if (ret) goto bailout; /* this is really icky. but it's what all the other callers to mk_error do. */ krberror.error = ret; krberror.error -= ERROR_TABLE_BASE_krb5; if (krberror.error < 0 || krberror.error > KRB_ERR_MAX) krberror.error = KRB_ERR_GENERIC; krberror.client = NULL; ret = krb5_build_principal(context, &krberror.server, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) goto bailout; krberror.text.length = 0; krberror.e_data = clear; ret = krb5_mk_error(context, &krberror, &cipher); krb5_free_principal(context, krberror.server); if (ret) goto bailout; } /* construct the reply */ ret = alloc_data(rep, 6 + ap_rep.length + cipher.length); if (ret) goto bailout; ptr = rep->data; /* length */ *ptr++ = (rep->length>>8) & 0xff; *ptr++ = rep->length & 0xff; /* version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* ap_rep length, big-endian */ *ptr++ = (ap_rep.length>>8) & 0xff; *ptr++ = ap_rep.length & 0xff; /* ap-rep data */ if (ap_rep.length) { memcpy(ptr, ap_rep.data, ap_rep.length); ptr += ap_rep.length; } /* krb-priv or krb-error */ memcpy(ptr, cipher.data, cipher.length); bailout: krb5_auth_con_free(context, auth_context); krb5_free_principal(context, changepw); krb5_free_ticket(context, ticket); free(ap_rep.data); free(clear.data); free(cipher.data); krb5_free_principal(context, target); krb5_free_unparsed_name(context, targetstr); krb5_free_unparsed_name(context, clientstr); krb5_free_error_message(context, errmsg); return ret; }
krb5_error_code krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data) { char *ptr; int plen, vno; krb5_data ap_rep; krb5_ap_rep_enc_part *ap_rep_enc; krb5_error_code ret; krb5_data cipherresult; krb5_data clearresult; krb5_error *krberror; krb5_replay_data replay; krb5_keyblock *tmp; if (packet->length < 4) /* either this, or the server is printing bad messages, or the caller passed in garbage */ return(KRB5KRB_AP_ERR_MODIFIED); ptr = packet->data; /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != packet->length) { /* * MS KDCs *may* send back a KRB_ERROR. Although * not 100% correct via RFC3244, it's something * we can workaround here. */ if (krb5_is_krb_error(packet)) { if ((ret = krb5_rd_error(context, packet, &krberror))) return(ret); if (krberror->e_data.data == NULL) { ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; krb5_free_error(context, krberror); return (ret); } } else { return(KRB5KRB_AP_ERR_MODIFIED); } } /* verify version number */ vno = (*ptr++ & 0xff); vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1) return(KRB5KDC_ERR_BAD_PVNO); /* read, check ap-rep length */ ap_rep.length = (*ptr++ & 0xff); ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff); if (ptr + ap_rep.length >= packet->data + packet->length) return(KRB5KRB_AP_ERR_MODIFIED); if (ap_rep.length) { /* verify ap_rep */ ap_rep.data = ptr; ptr += ap_rep.length; /* * Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp); if (ret) return ret; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { krb5_free_keyblock(context, tmp); return(ret); } krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* extract and decrypt the result */ cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; /* * Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp); krb5_free_keyblock(context, tmp); if (ret) return ret; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, &replay); if (ret) return(ret); } else { cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; if ((ret = krb5_rd_error(context, &cipherresult, &krberror))) return(ret); clearresult = krberror->e_data; } if (clearresult.length < 2) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } ptr = clearresult.data; *result_code = (*ptr++ & 0xff); *result_code = (*result_code<<8) | (*ptr++ & 0xff); if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* all success replies should be authenticated/encrypted */ if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } result_data->length = (clearresult.data + clearresult.length) - ptr; if (result_data->length) { result_data->data = (char *) malloc(result_data->length); if (result_data->data == NULL) { ret = ENOMEM; goto cleanup; } memcpy(result_data->data, ptr, result_data->length); } else { result_data->data = NULL; } ret = 0; cleanup: if (ap_rep.length) { krb5_xfree(clearresult.data); } else { krb5_free_error(context, krberror); } return(ret); }
krb5_error_code krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data ) { char *ptr; unsigned int message_length, version_number; krb5_data ap_rep; krb5_ap_rep_enc_part *ap_rep_enc; krb5_error_code ret; krb5_data cipherresult; krb5_data clearresult; krb5_keyblock *tmpkey; /* ** validate the packet length - */ if (packet->length < 4) return(KRB5KRB_AP_ERR_MODIFIED); ptr = packet->data; /* ** see if it is an error */ if (krb5_is_krb_error(packet)) { krb5_error *krberror; if ((ret = krb5_rd_error(context, packet, &krberror))) return(ret); if (krberror->e_data.data == NULL) { ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error; krb5_free_error(context, krberror); return (ret); } clearresult = krberror->e_data; krberror->e_data.data = NULL; /*So we can free it later*/ krberror->e_data.length = 0; krb5_free_error(context, krberror); } else { /* Not an error*/ /* ** validate the message length - ** length is big endian */ message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** make sure the message length and packet length agree - */ if (message_length != packet->length) return(KRB5KRB_AP_ERR_MODIFIED); /* ** get the version number - */ version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** make sure we support the version returned - */ /* ** set password version is 0xff80, change password version is 1 */ if (version_number != 1 && version_number != 0xff80) return(KRB5KDC_ERR_BAD_PVNO); /* ** now fill in ap_rep with the reply - */ /* ** get the reply length - */ ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** validate ap_rep length agrees with the packet length - */ if (ptr + ap_rep.length >= packet->data + packet->length) return(KRB5KRB_AP_ERR_MODIFIED); /* ** if data was returned, set the ap_rep ptr - */ if( ap_rep.length ) { ap_rep.data = ptr; ptr += ap_rep.length; /* * Save send_subkey to later smash recv_subkey. */ ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey); if (ret) return ret; ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc); if (ret) { krb5_free_keyblock(context, tmpkey); return(ret); } krb5_free_ap_rep_enc_part(context, ap_rep_enc); /* ** now decrypt the result - */ cipherresult.data = ptr; cipherresult.length = (packet->data + packet->length) - ptr; /* * Smash recv_subkey to be send_subkey, per spec. */ ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey); krb5_free_keyblock(context, tmpkey); if (ret) return ret; ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, NULL); if (ret) return(ret); } /*We got an ap_rep*/ else return (KRB5KRB_AP_ERR_MODIFIED); } /*Response instead of error*/ /* ** validate the cleartext length */ if (clearresult.length < 2) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* ** now decode the result - */ ptr = clearresult.data; *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff)); ptr += 2; /* ** result code 5 is access denied */ if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5)) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } /* ** all success replies should be authenticated/encrypted */ if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) ) { ret = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; } if (result_data) { result_data->length = (clearresult.data + clearresult.length) - ptr; if (result_data->length) { result_data->data = (char *) malloc(result_data->length); if (result_data->data) memcpy(result_data->data, ptr, result_data->length); } else result_data->data = NULL; } ret = 0; cleanup: krb5_free_data_contents(context, &clearresult); return(ret); }
static krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, int s, krb5_keytab keytab, struct sockaddr_in *sin, krb5_data *req, krb5_data *rep) { krb5_error_code ret; char *ptr; int plen, vno; krb5_address local_kaddr, remote_kaddr; int allocated_mem = 0; krb5_data ap_req, ap_rep; krb5_auth_context auth_context; krb5_principal changepw; krb5_ticket *ticket; krb5_data cipher, clear; struct sockaddr local_addr, remote_addr; int addrlen; krb5_replay_data replay; krb5_error krberror; int numresult; char strresult[1024]; ret = 0; rep->length = 0; auth_context = NULL; changepw = NULL; ap_rep.length = 0; ap_rep.data = NULL; ticket = NULL; clear.length = 0; clear.data = NULL; cipher.length = 0; cipher.data = NULL; if (req->length < 4) { /* * either this, or the server is printing bad messages, * or the caller passed in garbage */ ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; (void) strlcpy(strresult, "Request was truncated", sizeof (strresult)); goto chpwfail; } ptr = req->data; /* * Verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != req->length) return (KRB5KRB_AP_ERR_MODIFIED); /* * Verify version number */ vno = (*ptr++ & 0xff); vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1) { ret = KRB5KDC_ERR_BAD_PVNO; numresult = KRB5_KPASSWD_MALFORMED; (void) snprintf(strresult, sizeof (strresult), "Request contained unknown protocol version number %d", vno); goto chpwfail; } /* * Read, check ap-req length */ ap_req.length = (*ptr++ & 0xff); ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); if (ptr + ap_req.length >= req->data + req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; (void) strlcpy(strresult, "Request was truncated in AP-REQ", sizeof (strresult)); goto chpwfail; } /* * Verify ap_req */ ap_req.data = ptr; ptr += ap_req.length; if (ret = krb5_auth_con_init(context, &auth_context)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed initializing auth context: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed initializing auth context", sizeof (strresult)); goto chpwfail; } if (ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed setting auth " "context flags: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed initializing auth context", sizeof (strresult)); goto chpwfail; } if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm, "kadmin", "changepw", NULL)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed " "Failed to build kadmin/changepw " "principal: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed building kadmin/changepw principal", sizeof (strresult)); goto chpwfail; } ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, NULL, &ticket); if (ret) { char kt_name[MAX_KEYTAB_NAME_LEN]; if (krb5_kt_get_name(context, keytab, kt_name, sizeof (kt_name))) strncpy(kt_name, "default keytab", sizeof (kt_name)); switch (ret) { case KRB5_KT_NOTFOUND: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed because " "keytab entry \"kadmin/changepw\" " "is missing from \"%s\""), kt_name); break; case ENOENT: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed because " "keytab file \"%s\" does not exist"), kt_name); break; default: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed to parse Kerberos AP_REQ message: %s"), error_message(ret)); } numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Failed reading application request", sizeof (strresult)); goto chpwfail; } /* * Set up address info */ addrlen = sizeof (local_addr); if (getsockname(s, &local_addr, &addrlen) < 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed getting server internet address", sizeof (strresult)); goto chpwfail; } /* * Some brain-dead OS's don't return useful information from * the getsockname call. Namely, windows and solaris. */ if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) { local_kaddr.addrtype = ADDRTYPE_INET; local_kaddr.length = sizeof (((struct sockaddr_in *) &local_addr)->sin_addr); /* CSTYLED */ local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr); } else { krb5_address **addrs; krb5_os_localaddr(context, &addrs); local_kaddr.magic = addrs[0]->magic; local_kaddr.addrtype = addrs[0]->addrtype; local_kaddr.length = addrs[0]->length; if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for local_kaddr", sizeof (strresult)); goto chpwfail; } (void) memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); allocated_mem++; krb5_free_addresses(context, addrs); } addrlen = sizeof (remote_addr); if (getpeername(s, &remote_addr, &addrlen) < 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed getting client internet address", sizeof (strresult)); goto chpwfail; } remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof (((struct sockaddr_in *) &remote_addr)->sin_addr); /* CSTYLED */ remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr); remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof (sin->sin_addr); remote_kaddr.contents = (krb5_octet *) &sin->sin_addr; /* * mk_priv requires that the local address be set. * getsockname is used for this. rd_priv requires that the * remote address be set. recvfrom is used for this. If * rd_priv is given a local address, and the message has the * recipient addr in it, this will be checked. However, there * is simply no way to know ahead of time what address the * message will be delivered *to*. Therefore, it is important * that either no recipient address is in the messages when * mk_priv is called, or that no local address is passed to * rd_priv. Both is a better idea, and I have done that. In * summary, when mk_priv is called, *only* a local address is * specified. when rd_priv is called, *only* a remote address * is specified. Are we having fun yet? */ if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL, &remote_kaddr)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed storing client internet address", sizeof (strresult)); goto chpwfail; } /* * Verify that this is an AS_REQ ticket */ if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) { numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Ticket must be derived from a password", sizeof (strresult)); goto chpwfail; } /* * Construct the ap-rep */ if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) { numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Failed replying to application request", sizeof (strresult)); goto chpwfail; } /* * Decrypt the new password */ cipher.length = (req->data + req->length) - ptr; cipher.data = ptr; if (ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed decrypting request", sizeof (strresult)); goto chpwfail; } /* * Change the password */ if ((ptr = (char *)malloc(clear.length + 1)) == NULL) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for ptr", sizeof (strresult)); goto chpwfail; } (void) memcpy(ptr, clear.data, clear.length); ptr[clear.length] = '\0'; ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle, ticket->enc_part2->client, ptr, NULL, strresult, sizeof (strresult)); /* * Zap the password */ (void) memset(clear.data, 0, clear.length); (void) memset(ptr, 0, clear.length); if (clear.data != NULL) { krb5_xfree(clear.data); clear.data = NULL; } free(ptr); clear.length = 0; if (ret) { if ((ret != KADM5_PASS_Q_TOOSHORT) && (ret != KADM5_PASS_REUSE) && (ret != KADM5_PASS_Q_CLASS) && (ret != KADM5_PASS_Q_DICT) && (ret != KADM5_PASS_TOOSOON)) numresult = KRB5_KPASSWD_HARDERROR; else numresult = KRB5_KPASSWD_SOFTERROR; /* * strresult set by kadb5_chpass_principal_util() */ goto chpwfail; } /* * Success! */ numresult = KRB5_KPASSWD_SUCCESS; (void) strlcpy(strresult, "", sizeof (strresult)); chpwfail: clear.length = 2 + strlen(strresult); if (clear.data != NULL) { krb5_xfree(clear.data); clear.data = NULL; } if ((clear.data = (char *)malloc(clear.length)) == NULL) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for clear.data", sizeof (strresult)); } cipher.length = 0; if (ap_rep.length) { if (ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed storing client and server internet addresses", sizeof (strresult)); } else { if (ret = krb5_mk_priv(context, auth_context, &clear, &cipher, &replay)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed encrypting reply", sizeof (strresult)); } } } ptr = clear.data; *ptr++ = (numresult>>8) & 0xff; *ptr++ = numresult & 0xff; (void) memcpy(ptr, strresult, strlen(strresult)); /* * If no KRB-PRIV was constructed, then we need a KRB-ERROR. * If this fails, just bail. There's nothing else we can do. */ if (cipher.length == 0) { /* * Clear out ap_rep now, so that it won't be inserted * in the reply */ if (ap_rep.length) { if (ap_rep.data != NULL) krb5_xfree(ap_rep.data); ap_rep.data = NULL; ap_rep.length = 0; } krberror.ctime = 0; krberror.cusec = 0; krberror.susec = 0; if (ret = krb5_timeofday(context, &krberror.stime)) goto bailout; /* * This is really icky. but it's what all the other callers * to mk_error do. */ krberror.error = ret; krberror.error -= ERROR_TABLE_BASE_krb5; if (krberror.error < 0 || krberror.error > 128) krberror.error = KRB_ERR_GENERIC; krberror.client = NULL; if (ret = krb5_build_principal(context, &krberror.server, strlen(realm), realm, "kadmin", "changepw", NULL)) { goto bailout; } krberror.text.length = 0; krberror.e_data = clear; ret = krb5_mk_error(context, &krberror, &cipher); krb5_free_principal(context, krberror.server); if (ret) goto bailout; } /* * Construct the reply */ rep->length = 6 + ap_rep.length + cipher.length; if ((rep->data = (char *)malloc(rep->length)) == NULL) { ret = errno; goto bailout; } ptr = rep->data; /* * Length */ *ptr++ = (rep->length>>8) & 0xff; *ptr++ = rep->length & 0xff; /* * Version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* * ap_rep length, big-endian */ *ptr++ = (ap_rep.length>>8) & 0xff; *ptr++ = ap_rep.length & 0xff; /* * ap-rep data */ if (ap_rep.length) { (void) memcpy(ptr, ap_rep.data, ap_rep.length); ptr += ap_rep.length; } /* * krb-priv or krb-error */ (void) memcpy(ptr, cipher.data, cipher.length); bailout: if (auth_context) krb5_auth_con_free(context, auth_context); if (changepw) krb5_free_principal(context, changepw); if (ap_rep.data != NULL) krb5_xfree(ap_rep.data); if (ticket) krb5_free_ticket(context, ticket); if (clear.data != NULL) krb5_xfree(clear.data); if (cipher.data != NULL) krb5_xfree(cipher.data); if (allocated_mem) krb5_xfree(local_kaddr.contents); return (ret); }