summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGünther Deschner <gd@samba.org>2006-06-15 21:25:57 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 11:17:29 -0500
commite030a9e9dcda36edee475aa9fd8772f9f74b3552 (patch)
tree4953b5dcccfb818f399fdc65033742f902a2cb56
parent835bfbb8ac6a6239299a22a9e79643674a0a75e2 (diff)
downloadsamba-e030a9e9dcda36edee475aa9fd8772f9f74b3552.tar.gz
samba-e030a9e9dcda36edee475aa9fd8772f9f74b3552.tar.bz2
samba-e030a9e9dcda36edee475aa9fd8772f9f74b3552.zip
r16268: Add TCP fallback for our implementation of the CHANGEPW kpasswd calls.
This patch is mainly based on the work of Todd Stecher <tstecher@isilon.com> and has been reviewed by Jeremy. I sucessfully tested and valgrinded it with MIT 1.4.3, 1.3.5, Heimdal 0.7.2 and 0.6.1rc3. Guenther (This used to be commit 535d03cbe8b021e9aa6d74b62d81b867c494c957)
-rw-r--r--source3/configure.in13
-rw-r--r--source3/libads/krb5_setpw.c270
-rw-r--r--source3/libsmb/clikrb5.c59
3 files changed, 237 insertions, 105 deletions
diff --git a/source3/configure.in b/source3/configure.in
index 799441ed95..720e85e71a 100644
--- a/source3/configure.in
+++ b/source3/configure.in
@@ -3457,6 +3457,7 @@ if test x"$with_ads_support" != x"no"; then
AC_CHECK_FUNC_EXT(krb5_get_init_creds_opt_set_pac_request, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_get_renewed_creds, $KRB5_LIBS)
AC_CHECK_FUNC_EXT(krb5_get_kdc_cred, $KRB5_LIBS)
+ AC_CHECK_FUNC_EXT(krb5_free_error_contents, $KRB5_LIBS)
LIBS="$KRB5_LIBS $LIBS"
@@ -3506,6 +3507,18 @@ if test x"$with_ads_support" != x"no"; then
[Whether the krb5_ap_req struct has a ticket pointer])
fi
+ AC_CACHE_CHECK([for e_data pointer in krb5_error],
+ samba_cv_HAVE_E_DATA_POINTER_IN_KRB5_ERROR,[
+ AC_TRY_COMPILE([#include <krb5.h>],
+ [krb5_error err; err.e_data = NULL;],
+ samba_cv_HAVE_E_DATA_POINTER_IN_KRB5_ERROR=yes,
+ samba_cv_HAVE_E_DATA_POINTER_IN_KRB5_ERROR=no)])
+
+ if test x"$samba_cv_HAVE_E_DATA_POINTER_IN_KRB5_ERROR" = x"yes"; then
+ AC_DEFINE(HAVE_E_DATA_POINTER_IN_KRB5_ERROR,1,
+ [Whether the krb5_error struct has a e_data pointer])
+ fi
+
AC_CACHE_CHECK([for krb5_crypto type],
samba_cv_HAVE_KRB5_CRYPTO,[
AC_TRY_COMPILE([#include <krb5.h>],
diff --git a/source3/libads/krb5_setpw.c b/source3/libads/krb5_setpw.c
index ec2ff5afb1..07e6320c26 100644
--- a/source3/libads/krb5_setpw.c
+++ b/source3/libads/krb5_setpw.c
@@ -45,6 +45,14 @@
#define KRB5_KPASSWD_BAD_PRINCIPAL 9
#define KRB5_KPASSWD_ETYPE_NOSUPP 10
+/*
+ * we've got to be able to distinguish KRB_ERRORs from other
+ * requests - valid response for CHPW v2 replies.
+ */
+
+#define krb5_is_krb_error(packet) \
+ ( packet && packet->length && (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e))
+
/* This implements kerberos password change protocol as specified in
* kerb-chg-password-02.txt and kerberos-set-passwd-02.txt
* as well as microsoft version of the protocol
@@ -129,14 +137,16 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
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;
+ char *p, *msg_start;
DATA_BLOB setpw;
+ unsigned int msg_length;
ret = krb5_auth_con_setflags(context,
auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE);
@@ -172,12 +182,16 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
return ret;
}
- packet->data = (char *)SMB_MALLOC(ap_req->length + cipherpw.length + 6);
+ packet->data = (char *)SMB_MALLOC(ap_req->length + cipherpw.length + (use_tcp ? 10 : 6 ));
if (!packet->data)
return -1;
+
+
/* see the RFC for details */
- p = ((char *)packet->data) + 2;
+
+ msg_start = p = ((char *)packet->data) + (use_tcp ? 4 : 0);
+ p += 2;
RSSVAL(p, 0, pversion);
p += 2;
RSSVAL(p, 0, ap_req->length);
@@ -187,7 +201,12 @@ static krb5_error_code build_kpasswd_request(uint16 pversion,
memcpy(p, cipherpw.data, cipherpw.length);
p += cipherpw.length;
packet->length = PTR_DIFF(p,packet->data);
- RSSVAL(packet->data, 0, packet->length);
+ 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(...)' */
@@ -248,7 +267,8 @@ static krb5_error_code setpw_result_code_string(krb5_context context,
return KRB5KRB_ERR_GENERIC;
}
}
-static krb5_error_code parse_setpw_reply(krb5_context context,
+static krb5_error_code parse_setpw_reply(krb5_context context,
+ BOOL use_tcp,
krb5_auth_context auth_context,
krb5_data *packet)
{
@@ -259,23 +279,41 @@ static krb5_error_code parse_setpw_reply(krb5_context context,
krb5_data clearresult;
krb5_ap_rep_enc_part *ap_rep_enc;
krb5_replay_data replay;
-
- if (packet->length < 4) {
+ unsigned int msg_length = packet->length;
+
+
+ if (packet->length < (use_tcp ? 8 : 4)) {
return KRB5KRB_AP_ERR_MODIFIED;
}
p = packet->data;
+ /*
+ ** see if it is an error
+ */
+ if (krb5_is_krb_error(packet)) {
+
+ ret = handle_krberror_packet(context, packet);
+ if (ret) {
+ return ret;
+ }
+ }
+
- if (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e) {
- /* it's an error packet. We should parse it ... */
- DEBUG(1,("Got error packet 0x%x from kpasswd server\n",
- ((char *)packet->data)[0]));
- return KRB5KRB_AP_ERR_MODIFIED;
+ /* 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) != packet->length) {
+ if (RSVAL(p, 0) != msg_length) {
DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
- RSVAL(p, 0), packet->length));
+ RSVAL(p, 0), msg_length));
return KRB5KRB_AP_ERR_MODIFIED;
}
@@ -342,9 +380,9 @@ static krb5_error_code parse_setpw_reply(krb5_context context,
return KRB5KRB_AP_ERR_MODIFIED;
}
- if(res_code == KRB5_KPASSWD_SUCCESS)
- return 0;
- else {
+ 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));
@@ -365,7 +403,10 @@ static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
int ret, sock;
socklen_t addr_len;
struct sockaddr remote_addr, local_addr;
+ struct in_addr *addr = interpret_addr2(kdc_host);
krb5_address local_kaddr, remote_kaddr;
+ BOOL use_tcp = False;
+
ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
NULL, credsp, &ap_req);
@@ -373,102 +414,123 @@ static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
return ADS_ERROR_KRB5(ret);
}
-
- sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
- if (sock == -1) {
- int rc = errno;
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
- DEBUG(1,("failed to open kpasswd socket to %s (%s)\n",
- kdc_host, strerror(errno)));
- return ADS_ERROR_SYSTEM(rc);
- }
-
- addr_len = sizeof(remote_addr);
- getpeername(sock, &remote_addr, &addr_len);
- addr_len = sizeof(local_addr);
- getsockname(sock, &local_addr, &addr_len);
-
- setup_kaddr(&remote_kaddr, &remote_addr);
- setup_kaddr(&local_kaddr, &local_addr);
-
- ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
- if (ret) {
- close(sock);
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
- DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
- return ADS_ERROR_KRB5(ret);
- }
-
- ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
- princ, newpw, &chpw_req);
- if (ret) {
- close(sock);
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
- DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
- return ADS_ERROR_KRB5(ret);
- }
- if (write(sock, chpw_req.data, chpw_req.length) != chpw_req.length) {
- close(sock);
- free(chpw_req.data);
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
- DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
- return ADS_ERROR_SYSTEM(errno);
- }
+ do {
- free(chpw_req.data);
+ if (!use_tcp) {
- chpw_rep.length = 1500;
- chpw_rep.data = (char *) SMB_MALLOC(chpw_rep.length);
- if (!chpw_rep.data) {
- close(sock);
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
- DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
- errno = ENOMEM;
- return ADS_ERROR_SYSTEM(errno);
- }
+ sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
- ret = read(sock, chpw_rep.data, chpw_rep.length);
- if (ret < 0) {
- close(sock);
- free(chpw_rep.data);
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
- DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
- return ADS_ERROR_SYSTEM(errno);
- }
+ } else {
- close(sock);
- chpw_rep.length = ret;
+ sock = open_socket_out(SOCK_STREAM, addr, DEFAULT_KPASSWD_PORT,
+ LONG_CONNECT_TIMEOUT);
+ }
- ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
- if (ret) {
- free(chpw_rep.data);
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
- DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n",
- error_message(ret)));
- return ADS_ERROR_KRB5(ret);
- }
+ if (sock == -1) {
+ int rc = errno;
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("failed to open kpasswd socket to %s (%s)\n",
+ kdc_host, strerror(errno)));
+ return ADS_ERROR_SYSTEM(rc);
+ }
+
+ addr_len = sizeof(remote_addr);
+ getpeername(sock, &remote_addr, &addr_len);
+ addr_len = sizeof(local_addr);
+ getsockname(sock, &local_addr, &addr_len);
+
+ setup_kaddr(&remote_kaddr, &remote_addr);
+ setup_kaddr(&local_kaddr, &local_addr);
+
+ ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
+ if (ret) {
+ close(sock);
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
+ princ, newpw, use_tcp, &chpw_req);
+ if (ret) {
+ close(sock);
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
- ret = parse_setpw_reply(context, auth_context, &chpw_rep);
- free(chpw_rep.data);
+ ret = write(sock, chpw_req.data, chpw_req.length);
- if (ret) {
- free(ap_req.data);
+ if (ret != chpw_req.length) {
+ close(sock);
+ SAFE_FREE(chpw_req.data);
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ SAFE_FREE(chpw_req.data);
+
+ chpw_rep.length = 1500;
+ chpw_rep.data = (char *) SMB_MALLOC(chpw_rep.length);
+ if (!chpw_rep.data) {
+ close(sock);
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
+ errno = ENOMEM;
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ ret = read(sock, chpw_rep.data, chpw_rep.length);
+ if (ret < 0) {
+ close(sock);
+ SAFE_FREE(chpw_rep.data);
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
+ return ADS_ERROR_SYSTEM(errno);
+ }
+
+ close(sock);
+ chpw_rep.length = ret;
+
+ ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
+ if (ret) {
+ SAFE_FREE(chpw_rep.data);
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ ret = parse_setpw_reply(context, use_tcp, auth_context, &chpw_rep);
+ SAFE_FREE(chpw_rep.data);
+
+ if (ret) {
+
+ if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
+ DEBUG(5, ("Trying setpw with TCP!!!\n"));
+ use_tcp = True;
+ continue;
+ }
+
+ SAFE_FREE(ap_req.data);
+ krb5_auth_con_free(context, auth_context);
+ DEBUG(1,("parse_setpw_reply failed (%s)\n",
+ error_message(ret)));
+ return ADS_ERROR_KRB5(ret);
+ }
+
+ SAFE_FREE(ap_req.data);
krb5_auth_con_free(context, auth_context);
- DEBUG(1,("parse_setpw_reply failed (%s)\n",
- error_message(ret)));
- return ADS_ERROR_KRB5(ret);
- }
-
- free(ap_req.data);
- krb5_auth_con_free(context, auth_context);
+ } while ( ret );
return ADS_SUCCESS;
}
diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c
index e0d6b09d97..f1815b3e8f 100644
--- a/source3/libsmb/clikrb5.c
+++ b/source3/libsmb/clikrb5.c
@@ -1310,7 +1310,64 @@ done:
return ret;
}
-
+
+void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
+{
+#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
+ krb5_free_error_contents(context, krberror);
+#else /* MIT */
+ krb5_free_error(context, krberror);
+#endif
+}
+
+krb5_error_code handle_krberror_packet(krb5_context context,
+ krb5_data *packet)
+{
+ krb5_error_code ret;
+ BOOL got_error_code = False;
+
+ DEBUG(10,("handle_krberror_packet: got error packet\n"));
+
+#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
+ {
+ krb5_error krberror;
+
+ if ((ret = krb5_rd_error(context, packet, &krberror))) {
+ DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
+ ret = (krb5_error_code) krberror.error_code;
+ got_error_code = True;
+ }
+
+ smb_krb5_free_error(context, &krberror);
+ }
+#else /* MIT */
+ {
+ krb5_error *krberror;
+
+ if ((ret = krb5_rd_error(context, packet, &krberror))) {
+ DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
+ error_message(ret)));
+ return ret;
+ }
+
+ if (krberror->e_data.data == NULL) {
+ ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
+ got_error_code = True;
+ }
+ smb_krb5_free_error(context, krberror);
+ }
+#endif
+ if (got_error_code) {
+ DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n",
+ error_message(ret), ret));
+ }
+ return ret;
+}
#else /* HAVE_KRB5 */
/* this saves a few linking headaches */