X-Git-Url: http://andersk.mit.edu/gitweb/moira.git/blobdiff_plain/cd9e6b16ad69fb13d640f1b99c6616834a4d0137..f78c7eafcbfcd90ec8e555f8acd7af3cef0bbb33:/incremental/winad/setpw.c diff --git a/incremental/winad/setpw.c b/incremental/winad/setpw.c index 905e61aa..ed6af233 100644 --- a/incremental/winad/setpw.c +++ b/incremental/winad/setpw.c @@ -9,7 +9,7 @@ Copyright (C) 1999 Microsoft Corporation. All rights reserved. Module Name: - ksetpw.c + setpw.c Abstract: @@ -41,36 +41,44 @@ Abstract: * */ + #define NEED_SOCKETS #include #include +#include #ifdef _WIN32 +#include #include "k5-int.h" #include "adm_err.h" #include "krb5_err.h" -#endif -#include -#include "kpasswd.h" - -#ifndef _WIN32 +#else #include #include #include #endif - +#include #include #include #include #include #include - +#include "kpasswd.h" +#include "gsssasl.h" +#include "gssldap.h" #define PW_LENGTH 25 +#define KDC_PORT 464 +#define ULONG unsigned long #ifndef krb5_is_krb_error #define krb5_is_krb_error(dat)\ - ((dat) && (dat)->length && ((dat)->data[0] == 0x7e ||\ - (dat)->data[0] == 0x5e)) + ((dat) && (dat)->length && ((dat)->data[0] == 0x7e ||\ + (dat)->data[0] == 0x5e)) +#endif + +#ifdef _WIN32 +#define sleep(Seconds) Sleep(Seconds * 1000) +#define gethostbyname(Server) rgethostbyname(Server) #endif /* Win32 defines. */ @@ -147,34 +155,54 @@ static int frequency[26][26] = */ static int row_sums[26] = -{796, 160, 284, 401, 1276, 262, 199, 539, 777, - 16, 39, 351, 243, 751, 662, 181, 17, 683, - 662, 968, 248, 115, 180, 17, 162, 5}; +{796,160,284,401,1276,262,199,539,777, + 16,39,351,243,751,662,181,17,683, + 662,968,248,115,180,17,162,5}; /* * Frequencies of starting characters */ static int start_freq [26] = -{1299, 425, 725, 271, 375, 470, 93, 223, 1009, - 24, 20, 355, 379, 319, 823, 618, 21, 317, - 962, 1991, 271, 104, 516, 6, 16, 14}; +{1299,425,725,271,375,470,93,223,1009, + 24,20,355,379,319,823,618,21,317, + 962,1991,271,104,516,6,16,14}; /* * This MUST be equal to the sum of all elements in the above array. */ + +struct sockaddr_in kdc_server; +SOCKET kdc_socket; +krb5_context context; +krb5_ccache ccache; +krb5_auth_context auth_context = NULL; +krb5_data ap_req; +krb5_creds *credsp = NULL; +krb5_creds creds; +char connected_server[128]; + static int total_sum = 11646; +int get_krb5_error(krb5_error_code rc, char *in, char *out); +int ad_connect(LDAP **ldap_handle, char *ldap_domain, char *dn_path, + char *Win2kPassword, char *Win2kUser, char *default_server, + int connect_to_kdc); +int ad_kdc_connect(char *connectedServer); +int ad_server_connect(char *connectedServer, char *domain); +void ad_kdc_disconnect(); +int compare_elements(const void *arg1, const void *arg2); +int convert_domain_to_dn(char *domain, char *dnp); +int set_password(char *user, char *password, char *domain); + +int locate_ldap_server(char *domain, char **server_name); + long myrandom(); void generate_password(char *password); -int set_password(char *user, char *domain); krb5_error_code encode_krb5_setpw - PROTOTYPE((const krb5_setpw *rep, krb5_data ** code)); -krb5_error_code -krb5_locate_kpasswd(krb5_context context, const krb5_data *realm, - struct sockaddr **addr_pp, int *naddrs); + PROTOTYPE((const krb5_setpw *rep, krb5_data ** code)); -krb5_error_code krb5_mk_setpw_req(krb5_context context, krb5_auth_context auth_context, +krb5_error_code make_setpw_req(krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, krb5_principal targprinc, char *passwd, krb5_data *packet) { @@ -186,18 +214,18 @@ krb5_error_code krb5_mk_setpw_req(krb5_context context, krb5_auth_context auth_ char *ptr; register int count = 2; - memset (&setpw, 0, sizeof(krb5_setpw)); + memset(&setpw, 0, sizeof(krb5_setpw)); if (ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE)) - return(ret); + return(ret); setpw.targprinc = targprinc; setpw.newpasswd.length = strlen(passwd); setpw.newpasswd.data = passwd; if ((ret = encode_krb5_setpw(&setpw, &encoded_setpw))) - return( ret ); + return( ret ); if (ret = krb5_mk_priv(context, auth_context, - encoded_setpw, &cipherpw, &replay)) - return(ret); + encoded_setpw, &cipherpw, &replay)) + return(ret); packet->length = 6 + ap_req->length + cipherpw.length; packet->data = (char *) malloc(packet->length); ptr = packet->data; @@ -215,12 +243,15 @@ krb5_error_code krb5_mk_setpw_req(krb5_context context, krb5_auth_context auth_ ptr += ap_req->length; /* krb-priv of password */ memcpy(ptr, cipherpw.data, cipherpw.length); + free(cipherpw.data); +/* krb5_free_data_contents(context, &cipherpw);*/ + krb5_free_data(context, encoded_setpw); return(0); } -krb5_error_code krb5_rd_setpw_rep(krb5_context context, krb5_auth_context auth_context, - krb5_data *packet, int *result_code, - krb5_data *result_data) +krb5_error_code get_setpw_rep(krb5_context context, krb5_auth_context auth_context, + krb5_data *packet, int *result_code, + krb5_data *result_data) { char *ptr; int plen; @@ -235,379 +266,292 @@ krb5_error_code krb5_rd_setpw_rep(krb5_context context, krb5_auth_context auth_c krb5_ap_rep_enc_part *ap_rep_enc; if (packet->length < 4) - return(KRB5KRB_AP_ERR_MODIFIED); + return(KRB5KRB_AP_ERR_MODIFIED); ptr = packet->data; if (krb5_is_krb_error(packet)) { - ret = decode_krb5_error(packet, &krberror); - if (ret) - return(ret); - ret = krberror->error; - krb5_free_error(context, krberror); - return(ret); + ret = decode_krb5_error(packet, &krberror); + if (ret) + return(ret); + ret = krberror->error; + krb5_free_error(context, krberror); + return(ret); } /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != packet->length) - return(KRB5KRB_AP_ERR_MODIFIED); + return(KRB5KRB_AP_ERR_MODIFIED); vno = (*ptr++ & 0xff); vno = (vno<<8) | (*ptr++ & 0xff); if (vno != KRB5_KPASSWD_VERS_SETPW && vno != KRB5_KPASSWD_VERS_CHANGEPW) - return(KRB5KDC_ERR_BAD_PVNO); + 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); + return(KRB5KRB_AP_ERR_MODIFIED); if (ap_rep.length) { - /* verify ap_rep */ - ap_rep.data = ptr; - ptr += ap_rep.length; - if (ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc)) - 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; - /* XXX there's no api to do this right. The problem is that - if there's a remote subkey, it will be used. This is - not what the spec requires */ - tmp = auth_context->remote_subkey; - auth_context->remote_subkey = NULL; - ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, - &replay); - auth_context->remote_subkey = tmp; - if (ret) - return(ret); + /* verify ap_rep */ + ap_rep.data = ptr; + ptr += ap_rep.length; + if (ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc)) + 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; + /* XXX there's no api to do this right. The problem is that + if there's a remote subkey, it will be used. This is + not what the spec requires */ + tmp = auth_context->remote_subkey; + auth_context->remote_subkey = NULL; + ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult, + &replay); + auth_context->remote_subkey = tmp; + if (ret) + return(ret); } else { - cipherresult.data = ptr; - cipherresult.length = (packet->data + packet->length) - ptr; + cipherresult.data = ptr; + cipherresult.length = (packet->data + packet->length) - ptr; - if (ret = krb5_rd_error(context, &cipherresult, &krberror)) - return(ret); + if (ret = krb5_rd_error(context, &cipherresult, &krberror)) + return(ret); - clearresult = krberror->e_data; + clearresult = krberror->e_data; } if (clearresult.length < 2) { - ret = KRB5KRB_AP_ERR_MODIFIED; - goto cleanup; + 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_ACCESSDENIED)) + (*result_code > KRB5_KPASSWD_ACCESSDENIED)) { - ret = KRB5KRB_AP_ERR_MODIFIED; - goto cleanup; + 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; + 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); - memcpy(result_data->data, ptr, result_data->length); + result_data->data = (char *) malloc(result_data->length); + memcpy(result_data->data, ptr, result_data->length); } else - result_data->data = NULL; + result_data->data = NULL; ret = 0; cleanup: if (ap_rep.length) - free(clearresult.data); + free(clearresult.data); else - krb5_free_error(context, krberror); + krb5_free_error(context, krberror); return(ret); } -krb5_error_code krb5_set_password(krb5_context context, krb5_ccache ccache, +krb5_error_code kdc_set_password(krb5_context context, krb5_ccache ccache, char *newpw, char *user, char *domain, int *result_code) { - krb5_auth_context auth_context; - krb5_data ap_req; - krb5_data chpw_req; - krb5_data chpw_rep; + krb5_data chpw_snd; + krb5_data chpw_rcv; krb5_data result_string; krb5_address local_kaddr; krb5_address remote_kaddr; char userrealm[256]; char temp[256]; krb5_error_code code; - krb5_creds creds; - krb5_creds *credsp; - struct sockaddr *addr_p; struct sockaddr local_addr; struct sockaddr remote_addr; - struct sockaddr tmp_addr; - SOCKET s1; - SOCKET s2; int i; - int out; int addrlen; int cc; int local_result_code; - int tmp_len; - int error_count; + int nfds; krb5_principal targprinc; struct timeval TimeVal; fd_set readfds; - auth_context = NULL; - addr_p = NULL; - credsp = NULL; memset(&local_addr, 0, sizeof(local_addr)); memset(&local_kaddr, 0, sizeof(local_kaddr)); memset(&result_string, 0, sizeof(result_string)); memset(&remote_kaddr, 0, sizeof(remote_kaddr)); - memset(&chpw_req, 0, sizeof(krb5_data)); - memset(&chpw_rep, 0, sizeof(krb5_data)); - memset(&ap_req, 0, sizeof(krb5_data)); - auth_context = NULL; - memset(&creds, 0, sizeof(creds)); + memset(&chpw_snd, 0, sizeof(krb5_data)); + memset(&chpw_rcv, 0, sizeof(krb5_data)); memset(userrealm, '\0', sizeof(userrealm)); targprinc = NULL; + + chpw_rcv.length = 1500; + chpw_rcv.data = (char *) calloc(1, chpw_rcv.length); + for (i = 0; i < (int)strlen(domain); i++) - userrealm[i] = toupper(domain[i]); + userrealm[i] = toupper(domain[i]); sprintf(temp, "%s@%s", user, userrealm); krb5_parse_name(context, temp, &targprinc); - sprintf(temp, "%s@%s", "kadmin/changepw", userrealm); - if (code = krb5_parse_name(context, temp, &creds.server)) - goto cleanup; + if (credsp == NULL) + { + memset(&creds, 0, sizeof(creds)); + memset(&ap_req, 0, sizeof(krb5_data)); + sprintf(temp, "%s@%s", "kadmin/changepw", userrealm); + if (code = krb5_parse_name(context, temp, &creds.server)) + goto cleanup; + if (code = krb5_cc_get_principal(context, ccache, &creds.client)) + goto cleanup; + if (code = krb5_get_credentials(context, 0, ccache, &creds, &credsp)) + goto cleanup; + if (code = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, + NULL, credsp, &ap_req)) + goto cleanup; + } - if (code = krb5_cc_get_principal(context, ccache, &creds.client)) - goto cleanup; - if (code = krb5_get_credentials(context, 0, ccache, &creds, &credsp)) - goto cleanup; - if (code = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, - NULL, credsp, &ap_req)) - goto cleanup; - if (code = krb5_locate_kpasswd(context, &targprinc->realm, &addr_p, &out)) - goto cleanup; - if (out == 0) - { /* Couldn't resolve any KPASSWD names */ - code = 1; + addrlen = sizeof(local_addr); + if (getsockname(kdc_socket, &local_addr, &addrlen) < 0) + { + code = KDC_GETSOCKNAME_ERROR; goto cleanup; } - - /* this is really obscure. s1 is used for all communications. it - is left unconnected in case the server is multihomed and routes - are asymmetric. s2 is connected to resolve routes and get - addresses. this is the *only* way to get proper addresses for - multihomed hosts if routing is asymmetric. - - A related problem in the server, but not the client, is that - many os's have no way to disconnect a connected udp socket, so - the s2 socket needs to be closed and recreated for each - request. The s1 socket must not be closed, or else queued - requests will be lost. - - A "naive" client implementation (one socket, no connect, - hostname resolution to get the local ip addr) will work and - interoperate if the client is single-homed. */ - - if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) + if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) { - free(addr_p); - return(errno); + local_kaddr.addrtype = ADDRTYPE_INET; + local_kaddr.length = + sizeof(((struct sockaddr_in *) &local_addr)->sin_addr); + local_kaddr.contents = + (char *) &(((struct sockaddr_in *) &local_addr)->sin_addr); } - if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) + else { - closesocket(s1); - free(addr_p); - return(errno); + 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; + local_kaddr.contents = calloc(1, addrs[0]->length); + memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); + krb5_free_addresses(context, addrs); } - error_count = 0; - for (i=0; isin_addr); + remote_kaddr.contents = (char *) &(((struct sockaddr_in *) &remote_addr)->sin_addr); - addrlen = sizeof(local_addr); - if (getsockname(s2, &local_addr, &addrlen) < 0) - continue; - 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); - local_kaddr.contents = - (char *) &(((struct sockaddr_in *) &local_addr)->sin_addr); - } - else + if (code = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL)) + goto cleanup; + if (code = make_setpw_req(context, auth_context, &ap_req, + targprinc, newpw, &chpw_snd)) + goto cleanup; + + for (i = 0; i < 3; i++) + { + if ((cc = sendto(kdc_socket, chpw_snd.data, chpw_snd.length, 0, + NULL, + 0)) != chpw_snd.length) { - 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; - local_kaddr.contents = calloc(1, addrs[0]->length); - memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); - krb5_free_addresses(context, addrs); - } - - addrlen = sizeof(remote_addr); - if (getpeername(s2, &remote_addr, &addrlen) < 0) - continue; - remote_kaddr.addrtype = ADDRTYPE_INET; - remote_kaddr.length = - sizeof(((struct sockaddr_in *) &remote_addr)->sin_addr); - remote_kaddr.contents = - (char *) &(((struct sockaddr_in *) &remote_addr)->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 (code = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL)) - goto cleanup; - if (code = krb5_mk_setpw_req(context, auth_context, &ap_req, - targprinc, newpw, &chpw_req)) - goto cleanup; - if ((cc = sendto(s1, chpw_req.data, chpw_req.length, 0, - (struct sockaddr *) &addr_p[i], - sizeof(addr_p[i]))) != chpw_req.length) - continue; /* try the next addr */ -#ifdef _WIN32 - krb5_free_data_contents(context, &chpw_req); -#else - free(chpw_req.data); -#endif - chpw_rep.length = 1500; - chpw_rep.data = (char *) calloc(1, chpw_rep.length); - /* XXX need a timeout/retry loop here */ - /* "recv" would be good enough here... except that Windows/NT - commits the atrocity of returning -1 to indicate failure, - but leaving errno set to 0. - - "recvfrom(...,NULL,NULL)" would seem to be a good enough - alternative, and it works on NT, but it doesn't work on - SunOS 4.1.4 or Irix 5.3. Thus we must actually accept the - value and discard it. */ - tmp_len = sizeof(tmp_addr); - TimeVal.tv_sec = 10; - TimeVal.tv_usec = 0; + code = KDC_SEND_ERROR; + sleep(1); + continue; + } + TimeVal.tv_sec = 3; + TimeVal.tv_usec = 0; FD_ZERO(&readfds); - FD_SET(s1, &readfds); - code = select(1, &readfds, NULL, NULL, &TimeVal); - + FD_SET(kdc_socket, &readfds); + nfds = kdc_socket + 1; + code = select(nfds, &readfds, NULL, NULL, &TimeVal); if ((code == 0) || (code == SOCKET_ERROR)) { - if (error_count < 2) - { - closesocket(s1); - closesocket(s2); - if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) - goto cleanup; - if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) - goto cleanup; - ++error_count; - --i; - continue; - } - code = errno; - goto cleanup; + code = KDC_RECEIVE_TIMEOUT; + sleep(1); + continue; } - if ((cc = recvfrom(s1, chpw_rep.data, chpw_rep.length, 0, &tmp_addr, &tmp_len)) < 0) + + if ((cc = recvfrom(kdc_socket, chpw_rcv.data, chpw_rcv.length, 0, + NULL, NULL)) < 0) { - code = errno; - goto cleanup; - } - chpw_rep.length = cc; - if (code = krb5_auth_con_setaddrs(context, auth_context, NULL, - &remote_kaddr)) + code = KDC_RECEIVE_TIMEOUT; + sleep(1); + continue; + } + chpw_rcv.length = cc; + if (code = krb5_auth_con_setaddrs(context, auth_context, NULL, &remote_kaddr)) { - goto cleanup; - } + sleep(1); + continue; + } local_result_code = 0; - code = krb5_rd_setpw_rep(context, auth_context, &chpw_rep, - &local_result_code, &result_string); - - if (result_code) - *result_code = local_result_code; -#ifdef _WIN32 - krb5_free_data_contents(context, &chpw_req); -#else - free(chpw_req.data); -#endif - break; + code = get_setpw_rep(context, auth_context, &chpw_rcv, + &local_result_code, &result_string); + if (local_result_code) + { + if (local_result_code == KRB5_KPASSWD_SOFTERROR) + local_result_code = KRB5_KPASSWD_SUCCESS; + *result_code = local_result_code; + } + if ((code == 0) && (local_result_code == 0)) + break; + sleep(1); } + cleanup: - closesocket(s1); - closesocket(s2); - if (addr_p != NULL) - free(addr_p); - if (auth_context != NULL) - krb5_auth_con_free(context, auth_context); - if (ap_req.data != NULL) -#ifdef _WIN32 - krb5_free_data_contents(context, &ap_req); -#else - free(ap_req.data); -#endif - krb5_free_cred_contents(context, &creds); - if (credsp != NULL) - krb5_free_creds(context, credsp); + if (chpw_snd.data != NULL) + free(chpw_snd.data); + if (chpw_rcv.data != NULL) + free(chpw_rcv.data); if (targprinc != NULL) krb5_free_principal(context, targprinc); - return(code); + return(code); } -int set_password(char *user, char *domain) +int set_password(char *user, char *password, char *domain) { - krb5_context context; - krb5_ccache ccache; int res_code; krb5_error_code retval; - char pw[PW_LENGTH+1]; - - if (retval = krb5_init_context(&context)) - return retval; - if (retval = krb5_cc_default(context, &ccache)) - return(retval); + char pw[PW_LENGTH+1]; memset(pw, '\0', sizeof(pw)); - generate_password(pw); - retval = krb5_set_password(context, ccache, pw, user, domain, &res_code); + if (strlen(password) != 0) + strcpy(pw, password); + else + generate_password(pw); + res_code = 0; + retval = kdc_set_password(context, ccache, pw, user, domain, &res_code); - krb5_cc_close(context, ccache); - krb5_free_context(context); + if (res_code) + return(res_code); return(retval); } void generate_password(char *password) { - int i; + int i; int j; int row_position; int nchars; int position; - int word; + int word; int line; - char *pwp; + char *pwp; - for (line = 22; line; --line) + for (line = 22; line; --line) { for (word = 7; word; --word) { @@ -626,7 +570,7 @@ void generate_password(char *password) } *(++pwp)='\0'; return; - } + } putchar('\n'); } } @@ -655,3 +599,299 @@ long myrandom() } return (rand()); } + +int get_krb5_error(krb5_error_code rc, char *in, char *out) +{ + int krb5Error; + int retval; + + retval = 1; + + if (rc < 0) + { + krb5Error = ((int)(rc & 255)); + sprintf(out, "%s: %s(%ld)", in, error_message(rc), krb5Error); + } + else + { + switch (rc) + { + case KDC_RECEIVE_TIMEOUT: + { + retval = 0; + sprintf(out, "%s: %s(%d)", in, "Receive timeout", rc); + break; + } + case KDC_RECEIVE_ERROR: + { + retval = 0; + sprintf(out, "%s: %s(%d)", in, "Receive error", rc); + break; + } + case KRB5_KPASSWD_MALFORMED: + { + sprintf(out, "%s: %s(%d)", in, "malformed password", rc); + break; + } + case KRB5_KPASSWD_HARDERROR: + { + sprintf(out, "%s: %s(%d)", in, "hard error", rc); + break; + } + case KRB5_KPASSWD_AUTHERROR: + { + retval = 0; + sprintf(out, "%s: %s(%d)", in, "authentication error", rc); + break; + } + case KRB5_KPASSWD_SOFTERROR: + { + retval = 0; + sprintf(out, "%s: %s(%d)", in, "soft error", rc); + break; + } + case KRB5_KPASSWD_ACCESSDENIED: + { + sprintf(out, "%s: %s(%d)", in, "Access denied", rc); + break; + } + case KDC_SEND_ERROR: + { + retval = 0; + sprintf(out, "%s: %s(%d)", in, "Send error", rc); + break; + } + case KDC_GETSOCKNAME_ERROR: + { + retval = 0; + sprintf(out, "%s: %s(%d)", in, "Socket error - getsockname", rc); + break; + } + case KDC_GETPEERNAME_ERROR: + { + retval = 0; + sprintf(out, "%s: %s(%d)", in, "Socket error - getpeername", rc); + break; + } + default: + { + sprintf(out, "%s: %s(%d)", in, "unknown error", rc); + break; + } + } + } + return(retval); +} + +int ad_connect(LDAP **ldap_handle, char *ldap_domain, char *dn_path, + char *Win2kPassword, char *Win2kUser, char *default_server, + int connect_to_kdc) +{ + int i; + int j; + char *server_name[MAX_SERVER_NAMES]; + char server_array[MAX_SERVER_NAMES][256]; + static char temp[128]; + ULONG version = LDAP_VERSION3; + ULONG rc; + int Max_wait_time = 500; + int Max_size_limit = LDAP_NO_LIMIT; + + if (ldap_domain == NULL) + ldap_domain = "win.mit.edu"; + convert_domain_to_dn(ldap_domain, dn_path); + if (strlen(dn_path) == 0) + return(1); + + memset(server_name, 0, sizeof(server_name[0]) * MAX_SERVER_NAMES); + memset(server_array, 0, sizeof(server_array[0]) * MAX_SERVER_NAMES); + if (strlen(default_server) == 0) + { + if (locate_ldap_server(ldap_domain, server_name) == -1) + return(2); + j = 0; + for (i = 0; i < MAX_SERVER_NAMES; i++) + { + if (server_name[i] != NULL) + { + strcpy(server_array[i], server_name[i]); + free(server_name[i]); + j++; + } + } + if (j == 0) + return(2); +#ifdef _WIN32 + qsort((void *)server_array, (size_t)j, sizeof(server_array[0]), compare_elements); +#endif + } + else + strcpy(server_array[0], default_server); + + for (i = 0; i < MAX_SERVER_NAMES; i++) + { + if (strlen(server_array[i]) != 0) + { + if (((*ldap_handle) = ldap_open(server_array[i], LDAP_PORT)) != NULL) + { + rc = ldap_set_option((*ldap_handle), LDAP_OPT_PROTOCOL_VERSION, &version); + rc = ldap_set_option((*ldap_handle), LDAP_OPT_TIMELIMIT, + (void *)&Max_wait_time); + rc = ldap_set_option((*ldap_handle), LDAP_OPT_SIZELIMIT, + (void *)&Max_size_limit); + rc = ldap_set_option((*ldap_handle), LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + rc = ldap_adgssapi_bind((*ldap_handle), dn_path, GSSSASL_PRIVACY_PROTECTION); + if (rc == LDAP_SUCCESS) + { + if (connect_to_kdc) + { + if (!ad_server_connect(server_array[i], ldap_domain)) + { + ldap_unbind_s((*ldap_handle)); + continue; + } + } + if (strlen(default_server) == 0) + strcpy(default_server, server_array[i]); + strcpy(connected_server, server_array[i]); + break; + } + } + } + } + if (i >= MAX_SERVER_NAMES) + return(3); + return(0); +} + +int ad_server_connect(char *connectedServer, char *domain) +{ + krb5_error_code rc; + krb5_creds creds; + krb5_creds *credsp; + char temp[256]; + char userrealm[256]; + int i; + unsigned short port = KDC_PORT; + + context = NULL; + credsp = NULL; + memset(&ccache, 0, sizeof(ccache)); + memset(&creds, 0, sizeof(creds)); + memset(userrealm, '\0', sizeof(userrealm)); + + rc = 0; + if (krb5_init_context(&context)) + goto cleanup; + if (krb5_cc_default(context, &ccache)) + goto cleanup; + + for (i = 0; i < (int)strlen(domain); i++) + userrealm[i] = toupper(domain[i]); + sprintf(temp, "%s@%s", "kadmin/changepw", userrealm); + if (krb5_parse_name(context, temp, &creds.server)) + goto cleanup; + if (krb5_cc_get_principal(context, ccache, &creds.client)) + goto cleanup; + if (krb5_get_credentials(context, 0, ccache, &creds, &credsp)) + goto cleanup; + + rc = ad_kdc_connect(connectedServer); + + +cleanup: + if (!rc) + { + krb5_cc_close(context, ccache); + krb5_free_context(context); + } + krb5_free_cred_contents(context, &creds); + if (credsp != NULL) + krb5_free_creds(context, credsp); + return(rc); +} + + +int ad_kdc_connect(char *connectedServer) +{ + struct hostent *hp; + int rc; + + rc = 0; + hp = gethostbyname(connectedServer); + if (hp == NULL) + goto cleanup; + memset(&kdc_server, 0, sizeof(kdc_server)); + memcpy(&(kdc_server.sin_addr),hp->h_addr_list[0],hp->h_length); + kdc_server.sin_family = hp->h_addrtype; + kdc_server.sin_port = htons(KDC_PORT); + + if ((kdc_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) + goto cleanup; + if (connect(kdc_socket, (struct sockaddr*)&kdc_server, sizeof(kdc_server)) == SOCKET_ERROR) + goto cleanup; + rc = 1; + +cleanup: + return(rc); +} + +void ad_kdc_disconnect() +{ + + if (auth_context != NULL) + { + krb5_auth_con_free(context, auth_context); + if (ap_req.data != NULL) + free(ap_req.data); + krb5_free_cred_contents(context, &creds); + if (credsp != NULL) + krb5_free_creds(context, credsp); + } + credsp = NULL; + auth_context = NULL; + if (context != NULL) + { + krb5_cc_close(context, ccache); + krb5_free_context(context); + } + closesocket(kdc_socket); + +} + +int convert_domain_to_dn(char *domain, char *dnp) +{ + char *fp; + char *dp; + char dn[512]; + + memset(dn, '\0', sizeof(dn)); + strcpy(dn, "dc="); + dp = dn+3; + for (fp = domain; *fp; fp++) + { + if (*fp == '.') + { + strcpy(dp, ",dc="); + dp += 4; + } + else + *dp++ = *fp; + } + + strcpy(dnp, dn); + return 0; +} + +int compare_elements(const void *arg1, const void *arg2) +{ + int rc; + + rc = strcmp((char*)arg1, (char*)arg2); + if (rc < 0) + return(1); + if (rc > 0) + return(-1); + return(rc); +} +