diff options
author | Günther Deschner <gd@samba.org> | 2006-06-15 21:25:57 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 11:17:29 -0500 |
commit | e030a9e9dcda36edee475aa9fd8772f9f74b3552 (patch) | |
tree | 4953b5dcccfb818f399fdc65033742f902a2cb56 | |
parent | 835bfbb8ac6a6239299a22a9e79643674a0a75e2 (diff) | |
download | samba-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.in | 13 | ||||
-rw-r--r-- | source3/libads/krb5_setpw.c | 270 | ||||
-rw-r--r-- | source3/libsmb/clikrb5.c | 59 |
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 */ |