static NTSTATUS gensec_krb5_wrap(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; input.length = in->length; input.data = in->data; if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { ret = krb5_mk_priv(context, auth_context, &input, &output, NULL); if (ret) { DEBUG(1, ("krb5_mk_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; }
kadm5_ret_t _kadm5_client_send(kadm5_client_context *context, krb5_storage *sp) { krb5_data msg, out; krb5_error_code ret; size_t len; krb5_storage *sock; assert(context->sock != -1); len = krb5_storage_seek(sp, 0, SEEK_CUR); ret = krb5_data_alloc(&msg, len); if (ret) return ret; krb5_storage_seek(sp, 0, SEEK_SET); krb5_storage_read(sp, msg.data, msg.length); ret = krb5_mk_priv(context->context, context->ac, &msg, &out, NULL); krb5_data_free(&msg); if(ret) return ret; sock = krb5_storage_from_fd(context->sock); if(sock == NULL) { krb5_data_free(&out); return ENOMEM; } ret = krb5_store_data(sock, out); krb5_storage_free(sock); krb5_data_free(&out); return ret; }
static void reply_priv (krb5_auth_context auth_context, int s, struct sockaddr *sa, int sa_size, uint16_t result_code, const char *expl) { krb5_error_code ret; krb5_data krb_priv_data; krb5_data ap_rep_data; krb5_data e_data; ret = krb5_mk_rep (context, auth_context, &ap_rep_data); if (ret) { krb5_warn (context, ret, "Could not even generate error reply"); return; } if (make_result(&e_data, result_code, expl)) return; ret = krb5_mk_priv (context, auth_context, &e_data, &krb_priv_data, NULL); krb5_data_free (&e_data); if (ret) { krb5_warn (context, ret, "Could not even generate error reply"); return; } send_reply (s, sa, sa_size, &ap_rep_data, &krb_priv_data); krb5_data_free (&ap_rep_data); krb5_data_free (&krb_priv_data); }
/* * Now we send over the database. We use the following protocol: * Send over a KRB_SAFE message with the size. Then we send over the * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV. * Then we expect to see a KRB_SAFE message with the size sent back. * * At any point in the protocol, we may send a KRB_ERROR message; this * will abort the entire operation. */ static void xmit_database(krb5_context context, krb5_auth_context auth_context, krb5_creds *my_creds, int fd, int database_fd, int in_database_size) { krb5_int32 n; krb5_data inbuf, outbuf; char buf[KPROP_BUFSIZ]; krb5_error_code retval; krb5_error *error; krb5_ui_4 database_size = in_database_size, send_size, sent_size; /* Send over the size. */ send_size = htonl(database_size); inbuf.data = (char *)&send_size; inbuf.length = sizeof(send_size); /* must be 4, really */ /* KPROP_CKSUMTYPE */ retval = krb5_mk_safe(context, auth_context, &inbuf, &outbuf, NULL); if (retval) { com_err(progname, retval, _("while encoding database size")); send_error(context, my_creds, fd, _("while encoding database size"), retval); exit(1); } retval = krb5_write_message(context, &fd, &outbuf); if (retval) { krb5_free_data_contents(context, &outbuf); com_err(progname, retval, _("while sending database size")); exit(1); } krb5_free_data_contents(context, &outbuf); /* Initialize the initial vector. */ retval = krb5_auth_con_initivector(context, auth_context); if (retval) { send_error(context, my_creds, fd, "failed while initializing i_vector", retval); com_err(progname, retval, _("while allocating i_vector")); exit(1); } /* Send over the file, block by block. */ inbuf.data = buf; sent_size = 0; while ((n = read(database_fd, buf, sizeof(buf)))) { inbuf.length = n; retval = krb5_mk_priv(context, auth_context, &inbuf, &outbuf, NULL); if (retval) { snprintf(buf, sizeof(buf), "while encoding database block starting at %d", sent_size); com_err(progname, retval, "%s", buf); send_error(context, my_creds, fd, buf, retval); exit(1); } retval = krb5_write_message(context, &fd, &outbuf); if (retval) { krb5_free_data_contents(context, &outbuf); com_err(progname, retval, _("while sending database block starting at %d"), sent_size); exit(1); } krb5_free_data_contents(context, &outbuf); sent_size += n; if (debug) printf("%d bytes sent.\n", sent_size); } if (sent_size != database_size) { com_err(progname, 0, _("Premature EOF found for database file!")); send_error(context, my_creds, fd, "Premature EOF found for database file!", KRB5KRB_ERR_GENERIC); exit(1); } /* * OK, we've sent the database; now let's wait for a success * indication from the remote end. */ retval = krb5_read_message(context, &fd, &inbuf); if (retval) { com_err(progname, retval, _("while reading response from server")); exit(1); } /* * If we got an error response back from the server, display * the error message */ if (krb5_is_krb_error(&inbuf)) { retval = krb5_rd_error(context, &inbuf, &error); if (retval) { com_err(progname, retval, _("while decoding error response from server")); exit(1); } if (error->error == KRB_ERR_GENERIC) { if (error->text.data) { fprintf(stderr, _("Generic remote error: %s\n"), error->text.data); } } else if (error->error) { com_err(progname, (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5, _("signalled from server")); if (error->text.data) { fprintf(stderr, _("Error text from server: %s\n"), error->text.data); } } krb5_free_error(context, error); exit(1); } retval = krb5_rd_safe(context,auth_context,&inbuf,&outbuf,NULL); if (retval) { com_err(progname, retval, "while decoding final size packet from server"); exit(1); } memcpy(&send_size, outbuf.data, sizeof(send_size)); send_size = ntohl(send_size); if (send_size != database_size) { com_err(progname, 0, _("Kpropd sent database size %d, expecting %d"), send_size, database_size); exit(1); } free(outbuf.data); }
static int proto (int sock, const char *hostname, const char *service) { struct sockaddr_in remote, local; socklen_t addrlen; krb5_address remote_addr, local_addr; krb5_context context; krb5_ccache ccache; krb5_auth_context auth_context; krb5_error_code status; krb5_principal client; krb5_data data; krb5_data packet; krb5_creds mcred, cred; krb5_ticket *ticket; addrlen = sizeof(local); if (getsockname (sock, (struct sockaddr *)&local, &addrlen) < 0 || addrlen != sizeof(local)) err (1, "getsockname(%s)", hostname); addrlen = sizeof(remote); if (getpeername (sock, (struct sockaddr *)&remote, &addrlen) < 0 || addrlen != sizeof(remote)) err (1, "getpeername(%s)", hostname); status = krb5_init_context(&context); if (status) errx(1, "krb5_init_context failed: %d", status); status = krb5_cc_default (context, &ccache); if (status) krb5_err(context, 1, status, "krb5_cc_default"); status = krb5_auth_con_init (context, &auth_context); if (status) krb5_err(context, 1, status, "krb5_auth_con_init"); 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) krb5_err(context, 1, status, "krb5_auth_con_setaddr"); krb5_cc_clear_mcred(&mcred); status = krb5_cc_get_principal(context, ccache, &client); if(status) krb5_err(context, 1, status, "krb5_cc_get_principal"); status = krb5_make_principal(context, &mcred.server, krb5_principal_get_realm(context, client), "krbtgt", krb5_principal_get_realm(context, client), NULL); if(status) krb5_err(context, 1, status, "krb5_make_principal"); mcred.client = client; status = krb5_cc_retrieve_cred(context, ccache, 0, &mcred, &cred); if(status) krb5_err(context, 1, status, "krb5_cc_retrieve_cred"); { char *client_name; krb5_data data; status = krb5_unparse_name(context, cred.client, &client_name); if(status) krb5_err(context, 1, status, "krb5_unparse_name"); data.data = client_name; data.length = strlen(client_name) + 1; status = krb5_write_message(context, &sock, &data); if(status) krb5_err(context, 1, status, "krb5_write_message"); free(client_name); } status = krb5_write_message(context, &sock, &cred.ticket); if(status) krb5_err(context, 1, status, "krb5_write_message"); status = krb5_auth_con_setuserkey(context, auth_context, &cred.session); if(status) krb5_err(context, 1, status, "krb5_auth_con_setuserkey"); status = krb5_recvauth(context, &auth_context, &sock, VERSION, client, 0, NULL, &ticket); if (status) krb5_err(context, 1, status, "krb5_recvauth"); if (ticket->ticket.authorization_data) { AuthorizationData *authz; int i; printf("Authorization data:\n"); authz = ticket->ticket.authorization_data; for (i = 0; i < authz->len; i++) { printf("\ttype %d, length %lu\n", authz->val[i].ad_type, (unsigned long)authz->val[i].ad_data.length); } } data.data = "hej"; data.length = 3; krb5_data_zero (&packet); status = krb5_mk_safe (context, auth_context, &data, &packet, NULL); if (status) krb5_err(context, 1, status, "krb5_mk_safe"); status = krb5_write_message(context, &sock, &packet); if(status) krb5_err(context, 1, status, "krb5_write_message"); data.data = "hemligt"; data.length = 7; krb5_data_free (&packet); status = krb5_mk_priv (context, auth_context, &data, &packet, NULL); if (status) krb5_err(context, 1, status, "krb5_mk_priv"); status = krb5_write_message(context, &sock, &packet); if(status) krb5_err(context, 1, status, "krb5_write_message"); return 0; }
static int proto (int sock, const char *hostname, const char *service) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_data data; krb5_data packet; u_int32_t len, net_len; 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"); 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_sendauth (context, &auth_context, &sock, VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, NULL, NULL, NULL, NULL); if (status) krb5_err (context, 1, status, "krb5_sendauth"); data.data = "hej"; data.length = 3; krb5_data_zero (&packet); status = krb5_mk_safe (context, auth_context, &data, &packet, NULL); if (status) krb5_err (context, 1, status, "krb5_mk_safe"); len = packet.length; net_len = htonl(len); if (krb5_net_write (context, &sock, &net_len, 4) != 4) err (1, "krb5_net_write"); if (krb5_net_write (context, &sock, packet.data, len) != len) err (1, "krb5_net_write"); data.data = "hemligt"; data.length = 7; krb5_data_free (&packet); status = krb5_mk_priv (context, auth_context, &data, &packet, NULL); if (status) krb5_err (context, 1, status, "krb5_mk_priv"); len = packet.length; net_len = htonl(len); if (krb5_net_write (context, &sock, &net_len, 4) != 4) err (1, "krb5_net_write"); if (krb5_net_write (context, &sock, packet.data, len) != len) err (1, "krb5_net_write"); return 0; }
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 chgpw_send_request (krb5_context context, krb5_auth_context *auth_context, krb5_creds *creds, krb5_principal targprinc, int is_stream, int sock, char *passwd, const char *host) { krb5_error_code ret; krb5_data ap_req_data; krb5_data krb_priv_data; krb5_data passwd_data; size_t len; u_char header[6]; u_char *p; struct iovec iov[3]; struct msghdr msghdr; if (is_stream) return KRB5_KPASSWD_MALFORMED; if (targprinc && krb5_principal_compare(context, creds->client, targprinc) != TRUE) return KRB5_KPASSWD_MALFORMED; krb5_data_zero (&ap_req_data); ret = krb5_mk_req_extended (context, auth_context, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, /* in_data */ creds, &ap_req_data); if (ret) return ret; passwd_data.data = passwd; passwd_data.length = strlen(passwd); krb5_data_zero (&krb_priv_data); ret = krb5_mk_priv (context, *auth_context, &passwd_data, &krb_priv_data, NULL); if (ret) goto out2; len = 6 + ap_req_data.length + krb_priv_data.length; p = header; *p++ = (len >> 8) & 0xFF; *p++ = (len >> 0) & 0xFF; *p++ = 0; *p++ = 1; *p++ = (ap_req_data.length >> 8) & 0xFF; *p++ = (ap_req_data.length >> 0) & 0xFF; memset(&msghdr, 0, sizeof(msghdr)); msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = iov; msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); #if 0 msghdr.msg_control = NULL; msghdr.msg_controllen = 0; #endif iov[0].iov_base = (void*)header; iov[0].iov_len = 6; iov[1].iov_base = ap_req_data.data; iov[1].iov_len = ap_req_data.length; iov[2].iov_base = krb_priv_data.data; iov[2].iov_len = krb_priv_data.length; if (sendmsg (sock, &msghdr, 0) < 0) { ret = errno; krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret)); } krb5_data_free (&krb_priv_data); out2: krb5_data_free (&ap_req_data); return ret; }
static krb5_error_code setpw_send_request (krb5_context context, krb5_auth_context *auth_context, krb5_creds *creds, krb5_principal targprinc, int is_stream, int sock, char *passwd, const char *host) { krb5_error_code ret; krb5_data ap_req_data; krb5_data krb_priv_data; krb5_data pwd_data; ChangePasswdDataMS chpw; size_t len; u_char header[4 + 6]; u_char *p; struct iovec iov[3]; struct msghdr msghdr; krb5_data_zero (&ap_req_data); ret = krb5_mk_req_extended (context, auth_context, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, /* in_data */ creds, &ap_req_data); if (ret) return ret; chpw.newpasswd.length = strlen(passwd); chpw.newpasswd.data = passwd; if (targprinc) { chpw.targname = &targprinc->name; chpw.targrealm = &targprinc->realm; } else { chpw.targname = NULL; chpw.targrealm = NULL; } ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length, &chpw, &len, ret); if (ret) { krb5_data_free (&ap_req_data); return ret; } if(pwd_data.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_mk_priv (context, *auth_context, &pwd_data, &krb_priv_data, NULL); if (ret) goto out2; len = 6 + ap_req_data.length + krb_priv_data.length; p = header; if (is_stream) { _krb5_put_int(p, len, 4); p += 4; } *p++ = (len >> 8) & 0xFF; *p++ = (len >> 0) & 0xFF; *p++ = 0xff; *p++ = 0x80; *p++ = (ap_req_data.length >> 8) & 0xFF; *p++ = (ap_req_data.length >> 0) & 0xFF; memset(&msghdr, 0, sizeof(msghdr)); msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = iov; msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); #if 0 msghdr.msg_control = NULL; msghdr.msg_controllen = 0; #endif iov[0].iov_base = (void*)header; if (is_stream) iov[0].iov_len = 10; else iov[0].iov_len = 6; iov[1].iov_base = ap_req_data.data; iov[1].iov_len = ap_req_data.length; iov[2].iov_base = krb_priv_data.data; iov[2].iov_len = krb_priv_data.length; if (sendmsg (sock, &msghdr, 0) < 0) { ret = errno; krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret)); } krb5_data_free (&krb_priv_data); out2: krb5_data_free (&ap_req_data); krb5_data_free (&pwd_data); return ret; }
static krb5_error_code build_kpasswd_request(uint16 pversion, krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, const char *princ, const char *passwd, bool use_tcp, krb5_data *packet) { krb5_error_code ret; krb5_data cipherpw; krb5_data encoded_setpw; krb5_replay_data replay; char *p, *msg_start; DATA_BLOB setpw; unsigned int msg_length; ret = krb5_auth_con_setflags(context, auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { DEBUG(1,("krb5_auth_con_setflags failed (%s)\n", error_message(ret))); return ret; } /* handle protocol differences in chpw and setpw */ if (pversion == KRB5_KPASSWD_VERS_CHANGEPW) setpw = data_blob(passwd, strlen(passwd)); else if (pversion == KRB5_KPASSWD_VERS_SETPW || pversion == KRB5_KPASSWD_VERS_SETPW_ALT) setpw = encode_krb5_setpw(princ, passwd); else return EINVAL; if (setpw.data == NULL || setpw.length == 0) { return EINVAL; } encoded_setpw.data = (char *)setpw.data; encoded_setpw.length = setpw.length; ret = krb5_mk_priv(context, auth_context, &encoded_setpw, &cipherpw, &replay); data_blob_free(&setpw); /*from 'encode_krb5_setpw(...)' */ if (ret) { DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret))); return ret; } packet->data = (char *)SMB_MALLOC(ap_req->length + cipherpw.length + (use_tcp ? 10 : 6 )); if (!packet->data) return -1; /* see the RFC for details */ msg_start = p = ((char *)packet->data) + (use_tcp ? 4 : 0); p += 2; RSSVAL(p, 0, pversion); p += 2; RSSVAL(p, 0, ap_req->length); p += 2; memcpy(p, ap_req->data, ap_req->length); p += ap_req->length; memcpy(p, cipherpw.data, cipherpw.length); p += cipherpw.length; packet->length = PTR_DIFF(p,packet->data); msg_length = PTR_DIFF(p,msg_start); if (use_tcp) { RSIVAL(packet->data, 0, msg_length); } RSSVAL(msg_start, 0, msg_length); free(cipherpw.data); /* from 'krb5_mk_priv(...)' */ return 0; }
static krb5_error_code chgpw_prexmit(krb5_context context, int proto, void *ctx, rk_socket_t fd, krb5_data *data) { struct request *request = ctx; krb5_data ap_req_data, krb_priv_data, passwd_data; krb5_storage *sp = NULL; krb5_error_code ret; krb5_ssize_t slen; size_t len; krb5_data_zero(&ap_req_data); krb5_data_zero(&krb_priv_data); ret = krb5_auth_con_genaddrs(context, request->ac, fd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); if (ret) goto out; ret = krb5_mk_req_extended(context, &request->ac, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, request->creds, &ap_req_data); if (ret) goto out; passwd_data.data = rk_UNCONST(request->password); passwd_data.length = strlen(request->password); ret = krb5_mk_priv(context, request->ac, &passwd_data, &krb_priv_data, NULL); if (ret) goto out; sp = krb5_storage_emem(); if (sp == NULL) { ret = ENOMEM; goto out; } len = 6 + ap_req_data.length + krb_priv_data.length; ret = krb5_store_uint16(sp, len); if (ret) goto out; ret = krb5_store_uint16(sp, 1); if (ret) goto out; ret = krb5_store_uint16(sp, ap_req_data.length); if (ret) goto out; slen = krb5_storage_write(sp, ap_req_data.data, ap_req_data.length); if (slen != ap_req_data.length) { ret = EINVAL; goto out; } slen = krb5_storage_write(sp, krb_priv_data.data, krb_priv_data.length); if (slen != krb_priv_data.length) { ret = EINVAL; goto out; } ret = krb5_storage_to_data(sp, data); out: if (ret) _krb5_debugx(context, 10, "chgpw_prexmit failed with: %d", ret); if (sp) krb5_storage_free(sp); krb5_data_free(&krb_priv_data); krb5_data_free(&ap_req_data); return ret; }
static krb5_error_code setpw_prexmit(krb5_context context, int proto, void *ctx, int fd, krb5_data *data) { struct request *request = ctx; krb5_data ap_req_data, krb_priv_data, pwd_data; krb5_error_code ret; ChangePasswdDataMS chpw; krb5_storage *sp = NULL; ssize_t slen; size_t len; krb5_data_zero(&ap_req_data); krb5_data_zero(&krb_priv_data); krb5_data_zero(&pwd_data); ret = krb5_auth_con_genaddrs(context, request->ac, fd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); if (ret) goto out; ret = krb5_mk_req_extended(context, &request->ac, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, request->creds, &ap_req_data); if (ret) goto out; chpw.newpasswd.length = strlen(request->password); chpw.newpasswd.data = rk_UNCONST(request->password); if (request->target) { chpw.targname = &request->target->name; chpw.targrealm = &request->target->realm; } else { chpw.targname = NULL; chpw.targrealm = NULL; } ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length, &chpw, &len, ret); if (ret) goto out; if(pwd_data.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_mk_priv (context, request->ac, &pwd_data, &krb_priv_data, NULL); if (ret) goto out; sp = krb5_storage_emem(); if (sp == NULL) { ret = ENOMEM; goto out; } len = 6 + ap_req_data.length + krb_priv_data.length; ret = krb5_store_uint16(sp, len); if (ret) goto out; ret = krb5_store_uint16(sp, 0xff80); if (ret) goto out; ret = krb5_store_uint16(sp, ap_req_data.length); if (ret) goto out; slen = krb5_storage_write(sp, ap_req_data.data, ap_req_data.length); if (slen != ap_req_data.length) { ret = EINVAL; goto out; } slen = krb5_storage_write(sp, krb_priv_data.data, krb_priv_data.length); if (slen != krb_priv_data.length) { ret = EINVAL; goto out; } ret = krb5_storage_to_data(sp, data); out: if (ret) _krb5_debugx(context, 10, "setpw_prexmit failed with %d", ret); if (sp) krb5_storage_free(sp); krb5_data_free(&krb_priv_data); krb5_data_free(&ap_req_data); krb5_data_free(&pwd_data); return ret; }
krb5_error_code krb5int_mk_setpw_req(krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, krb5_principal targprinc, char *passwd, krb5_data *packet) { krb5_error_code ret; krb5_data cipherpw; krb5_data *encoded_setpw; struct krb5_setpw_req req; char *ptr; cipherpw.data = NULL; cipherpw.length = 0; if ((ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE))) return(ret); req.target = targprinc; req.password.data = passwd; req.password.length = strlen(passwd); ret = encode_krb5_setpw_req(&req, &encoded_setpw); if (ret) { return ret; } if ((ret = krb5_mk_priv(context, auth_context, encoded_setpw, &cipherpw, NULL)) != 0) { krb5_free_data(context, encoded_setpw); return(ret); } krb5_free_data(context, encoded_setpw); packet->length = 6 + ap_req->length + cipherpw.length; packet->data = (char *) malloc(packet->length); if (packet->data == NULL) { ret = ENOMEM; goto cleanup; } ptr = packet->data; /* ** build the packet - */ /* put in the length */ store_16_be(packet->length, ptr); ptr += 2; /* put in the version */ *ptr++ = (char)0xff; *ptr++ = (char)0x80; /* the ap_req length is big endian */ store_16_be(ap_req->length, ptr); ptr += 2; /* put in the request data */ memcpy(ptr, ap_req->data, ap_req->length); ptr += ap_req->length; /* ** put in the "private" password data - */ memcpy(ptr, cipherpw.data, cipherpw.length); ret = 0; cleanup: if (cipherpw.data) krb5_free_data_contents(context, &cipherpw); if ((ret != 0) && packet->data) { free(packet->data); packet->data = NULL; } return ret; }
krb5_error_code krb5int_mk_chpw_req(krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, char *passwd, krb5_data *packet) { krb5_error_code ret = 0; krb5_data clearpw; krb5_data cipherpw; krb5_replay_data replay; char *ptr; cipherpw.data = NULL; if ((ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE))) goto cleanup; clearpw.length = strlen(passwd); clearpw.data = passwd; if ((ret = krb5_mk_priv(context, auth_context, &clearpw, &cipherpw, &replay))) goto cleanup; packet->length = 6 + ap_req->length + cipherpw.length; packet->data = (char *) malloc(packet->length); if (packet->data == NULL) { ret = ENOMEM; goto cleanup; } ptr = packet->data; /* length */ store_16_be(packet->length, ptr); ptr += 2; /* version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* ap_req length, big-endian */ store_16_be(ap_req->length, ptr); ptr += 2; /* ap-req data */ memcpy(ptr, ap_req->data, ap_req->length); ptr += ap_req->length; /* krb-priv of password */ memcpy(ptr, cipherpw.data, cipherpw.length); cleanup: if (cipherpw.data != NULL) /* allocated by krb5_mk_priv */ free(cipherpw.data); 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; }
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); }