diff options
Diffstat (limited to 'src/windows/wintel/auth.c')
-rw-r--r-- | src/windows/wintel/auth.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/src/windows/wintel/auth.c b/src/windows/wintel/auth.c new file mode 100644 index 000000000..f3956ac11 --- /dev/null +++ b/src/windows/wintel/auth.c @@ -0,0 +1,626 @@ +/* + * Implements Kerberos 4 authentication + */ + +#include <time.h> +#include <string.h> +#ifdef KRB4 + #include "kerberos.h" +#endif +#include "telnet.h" +#include "telopts.h" + +#ifdef KRB5 + #include "krb5.h" + #include "des_int.h" + #include "com_err.h" + #include "los-proto.h" +#endif + +/* + * Contants + */ + #define IS 0 + #define SEND 1 + #define REPLY 2 + #define NAME 3 + + #define AUTH_NULL 0 + #define KERBEROS_V4 1 + #define KERBEROS_V5 2 + #define SPX 3 + #define RSA 6 + #define LOKI 10 + + #define AUTH 0 + #define K4_REJECT 1 + #define K4_ACCEPT 2 + #define K4_CHALLENGE 3 + #define K4_RESPONSE 4 + + #define K5_REJECT 1 + #define K5_ACCEPT 2 + #define K5_RESPONSE 3 + + #define AUTH_WHO_MASK 1 + #define AUTH_CLIENT_TO_SERVER 0 + #define AUTH_SERVER_TO_CLIENT 1 + + #define AUTH_HOW_MASK 2 + #define AUTH_HOW_ONE_WAY 0 + #define AUTH_HOW_MUTUAL 2 + + #ifndef KSUCCESS + #define KSUCCESS 0 + #define KFAILURE 255 + #endif +/* + * Globals + */ + #ifdef KRB4 + static CREDENTIALS cred; + #define KRB_SERVICE_NAME "rcmd" + #define KERBEROS_VERSION KERBEROS_V4 + #endif + #ifdef KRB5 + static krb5_data auth; + static int auth_how; + #define KRB_SERVICE_NAME "host" + #define KERBEROS_VERSION KERBEROS_V5 + #endif + + BOOL encrypt_enable; + +/*+ + * Function: Enable or disable the encryption process. + * + * Parameters: + * enable - TRUE to enable, FALSE to disable. + */ +static void auth_encrypt_enable( + BOOL enable) +{ + encrypt_enable = enable; + +} /* auth_encrypt_enable */ + + +/*+ + * Function: Abort the authentication process + * + * Parameters: + * ks - kstream to send abort message to. + */ +static void auth_abort( + kstream ks, + char *errmsg, + long r) +{ + char buf[9]; + + wsprintf(buf, "%c%c%c%c%c%c%c%c", IAC, SB, AUTHENTICATION, IS, AUTH_NULL, AUTH_NULL, IAC, SE); + TelnetSend(ks, (LPSTR)buf, 8, 0); + + if (errmsg != NULL) { + strcpy(strTmp, errmsg); + + if (r != KSUCCESS) { + strcat(strTmp, "\n"); + #ifdef KRB4 + lstrcat(strTmp, krb_get_err_text(r)); + #endif + #ifdef KRB5 + lstrcat (strTmp, error_message(r)); + #endif + } + + MessageBox(HWND_DESKTOP, strTmp, "Kerberos authentication failed!", MB_OK | MB_ICONEXCLAMATION); + } + +} /* auth_abort */ + + +/*+ + * Function: Copy data to buffer, doubling IAC character if present. + * + * Parameters: + * kstream - kstream to send abort message to. + */ +static int copy_for_net( + unsigned char *to, + unsigned char *from, + int c) +{ + int n; + + n = c; + + while (c-- > 0) { + if ((*to++ = *from++) == IAC) { + n++; + *to++ = IAC; + } + } + + return n; + +} /* copy_for_net */ + + +/*+ + * Function: Parse authentication send command + * + * Parameters: + * ks - kstream to send abort message to. + * + * parsedat - the sub-command data. + * + * end_sub - index of the character in the 'parsedat' array which + * is the last byte in a sub-negotiation + * + * Returns: Kerberos error code. + */ +static int auth_send( + kstream ks, + unsigned char *parsedat, + int end_sub) +{ + char buf[512]; + char *pname; + int plen; + int r; + int i; + #ifdef KRB4 + KTEXT_ST auth; + char instance[INST_SZ]; + char *realm; + #endif /* KRB4 */ + #ifdef KRB5 + extern int kerberos5_send (int how); + #endif /* KRB5 */ + + auth_how = -1; + + for (i = 2; i+1 <= end_sub; i += 2) { + if (parsedat[i] == KERBEROS_VERSION) + if ((parsedat[i+1] & AUTH_WHO_MASK) == AUTH_CLIENT_TO_SERVER) { + auth_how = parsedat[i+1] & AUTH_HOW_MASK; + break; + } + } + + if (auth_how == -1) { + auth_abort(ks, NULL, 0); + return KFAILURE; + } + + #ifdef KRB4 + memset(instance, 0, sizeof(instance)); + + if (realm = krb_get_phost(szHostName)) + lstrcpy(instance, realm); + + realm = krb_realmofhost(szHostName); + + if (!realm) { + strcpy(buf, "Can't find realm for host \""); + strcat(buf, szHostName); + strcat(buf, "\""); + auth_abort(ks, buf, 0); + return KFAILURE; + } + + r = krb_mk_req(&auth, KRB_SERVICE_NAME, instance, realm, 0); + + if (r == 0) + r = krb_get_cred(KRB_SERVICE_NAME, instance, realm, &cred); + + if (r) { + strcpy(buf, "Can't get \""); + strcat(buf, KRB_SERVICE_NAME); + if (instance[0] != 0) { + strcat(buf, "."); + lstrcat(buf, instance); + } + strcat(buf, "@"); + lstrcat(buf, realm); + strcat(buf, "\" ticket"); + auth_abort(ks, buf, r); + return r; + } + + if (szUserName[0]) + pname = szUserName; + else + pname = cred.pname; + plen = strlen (szUserName); + + #endif /* KRB4 */ + + #ifdef KRB5 + r = kerberos5_send (auth_how); + if (! r) + return KFAILURE; + + plen = strlen (szUserName); /* Set in kerberos_5 if needed */ + pname = szUserName; + + #endif /* KRB5 */ + + wsprintf(buf, "%c%c%c%c", IAC, SB, AUTHENTICATION, NAME); + memcpy (&buf[4], pname, plen); + wsprintf(&buf[plen + 4], "%c%c", IAC, SE); + TelnetSend(ks, (LPSTR)buf, lstrlen(pname)+6, 0); + + wsprintf(buf, "%c%c%c%c%c%c%c", IAC, SB, AUTHENTICATION, IS, + KERBEROS_VERSION, auth_how | AUTH_CLIENT_TO_SERVER, AUTH); + + #if KRB4 + auth.length = copy_for_net(&buf[7], auth.dat, auth.length); + #endif /* KRB4 */ + #if KRB5 + auth.length = copy_for_net(&buf[7], auth.data, auth.length); + #endif /* KRB5 */ + + wsprintf(&buf[auth.length+7], "%c%c", IAC, SE); + + TelnetSend(ks, (LPSTR)buf, auth.length+9, 0); + + return KSUCCESS; + +} /* auth_send */ + +/*+ + * Function: Parse authentication reply command + * + * Parameters: + * ks - kstream to send abort message to. + * + * parsedat - the sub-command data. + * + * end_sub - index of the character in the 'parsedat' array which + * is the last byte in a sub-negotiation + * + * Returns: Kerberos error code. + */ +#ifdef KRB5 +static int auth_reply( + kstream ks, + unsigned char *parsedat, + int end_sub) +{ + extern int kerberos5_reply (int how, unsigned char *data, int cnt); + int n; + + n = kerberos5_reply (0, parsedat, end_sub); + + return n; +} +#endif /* KRB5 */ +#ifdef KRB4 +static int auth_reply( + kstream ks, + unsigned char *parsedat, + int end_sub) +{ + time_t t; + int x; + char buf[512]; + int i; + des_cblock session_key; + des_key_schedule sched; + static des_cblock challenge; + + if (end_sub < 4) + return KFAILURE; + + if (parsedat[2] != KERBEROS_V4) + return KFAILURE; + + if (parsedat[4] == K4_REJECT) { + buf[0] = 0; + + for (i = 5; i <= end_sub; i++) { + if (parsedat[i] == IAC) + break; + buf[i-5] = parsedat[i]; + buf[i-4] = 0; + } + + if (!buf[0]) + strcpy(buf, "Authentication rejected by remote machine!"); + MessageBox(HWND_DESKTOP, buf, NULL, MB_OK | MB_ICONEXCLAMATION); + + return KFAILURE; + } + + if (parsedat[4] == K4_ACCEPT) { + if ((parsedat[3] & AUTH_HOW_MASK) == AUTH_HOW_ONE_WAY) + return KSUCCESS; + + if ((parsedat[3] & AUTH_HOW_MASK) != AUTH_HOW_MUTUAL) + return KFAILURE; + + des_key_sched(cred.session, sched); + + t = time(NULL); + memcpy(challenge, &t, 4); + memcpy(&challenge[4], &t, 4); + des_ecb_encrypt(&challenge, &session_key, sched, 1); + + /* + * Increment the challenge by 1, and encrypt it for + * later comparison. + */ + for (i = 7; i >= 0; --i) { + x = (unsigned int)challenge[i] + 1; + challenge[i] = x; /* ignore overflow */ + if (x < 256) /* if no overflow, all done */ + break; + } + + des_ecb_encrypt(&challenge, &challenge, sched, 1); + + wsprintf(buf, "%c%c%c%c%c%c%c", IAC, SB, AUTHENTICATION, IS, + KERBEROS_V4, AUTH_CLIENT_TO_SERVER|AUTH_HOW_MUTUAL, K4_CHALLENGE); + memcpy(&buf[7], session_key, 8); + wsprintf(&buf[15], "%c%c", IAC, SE); + TelnetSend(ks, (LPSTR)buf, 17, 0); + + return KSUCCESS; + } + + if (parsedat[4] == K4_RESPONSE) { + if (end_sub < 12) + return KFAILURE; + + if (memcmp(&parsedat[5], challenge, sizeof(challenge)) != 0) { + MessageBox(HWND_DESKTOP, "Remote machine is being impersonated!", + NULL, MB_OK | MB_ICONEXCLAMATION); + + return KFAILURE; + } + + return KSUCCESS; + } + + return KFAILURE; + +} /* auth_reply */ + +#endif /* KRB4 */ +/*+ + * Function: Parse the athorization sub-options and reply. + * + * Parameters: + * ks - kstream to send abort message to. + * + * parsedat - sub-option string to parse. + * + * end_sub - last charcter position in parsedat. + */ +void auth_parse( + kstream ks, + unsigned char *parsedat, + int end_sub) +{ + if (parsedat[1] == SEND) + auth_send(ks, parsedat, end_sub); + + if (parsedat[1] == REPLY) + auth_reply(ks, parsedat, end_sub); + +} /* auth_parse */ + + +/*+ + * Function: Initialization routine called kstream encryption system. + * + * Parameters: + * str - kstream to send abort message to. + * + * data - user data. + */ +int INTERFACE auth_init( + kstream str, + kstream_ptr data) +{ + return 0; + +} /* auth_init */ + + +/*+ + * Function: Destroy routine called kstream encryption system. + * + * Parameters: + * str - kstream to send abort message to. + * + * data - user data. + */ +void INTERFACE auth_destroy( + kstream str) +{ +} /* auth_destroy */ + + +/*+ + * Function: Callback to encrypt a block of characters + * + * Parameters: + * out - return as pointer to converted buffer. + * + * in - the buffer to convert + * + * str - the stream being encrypted + * + * Returns: number of characters converted. + */ +int INTERFACE auth_encrypt( + struct kstream_data_block *out, + struct kstream_data_block *in, + kstream str) +{ + out->ptr = in->ptr; + + out->length = in->length; + + return(out->length); + +} /* auth_encrypt */ + + +/*+ + * Function: Callback to decrypt a block of characters + * + * Parameters: + * out - return as pointer to converted buffer. + * + * in - the buffer to convert + * + * str - the stream being encrypted + * + * Returns: number of characters converted. + */ +int INTERFACE auth_decrypt( + struct kstream_data_block *out, + struct kstream_data_block *in, + kstream str) +{ + out->ptr = in->ptr; + + out->length = in->length; + + return(out->length); + +} /* auth_decrypt */ + +/*+*/ +#ifdef KRB5 + +/* +** +** Code lifted from telnet sample code in the appl directory. +** +*/ + +krb5_auth_context *auth_context; +krb5_flags krb5_kdc_default_options = KDC_OPT_RENEWABLE_OK; + +/* 0 on failure, 1 on success */ +int +kerberos5_send (int how) +{ + krb5_error_code r; + krb5_ccache ccache; + krb5_creds cred; + krb5_creds * new_cred; + extern krb5_flags krb5_kdc_default_options; + krb5_flags ap_opts; + int len; + + if (r = krb5_cc_default(k5_context, &ccache)) { + com_err (NULL, r, "while authorizing."); + return(0); + } + + memset((char *)&cred, 0, sizeof(cred)); + if (r = krb5_sname_to_principal(k5_context, szHostName, KRB_SERVICE_NAME, + KRB5_NT_SRV_HST, &cred.server)) { + com_err (NULL, r, "while authorizing."); + return(0); + } + + if (r = krb5_cc_get_principal(k5_context, ccache, &cred.client)) { + com_err (NULL, r, "while authorizing."); + krb5_free_cred_contents(k5_context, &cred); + return(0); + } + if (szUserName[0] == '\0') { /* Get user name now */ + len = krb5_princ_component(k5_context, cred.client, 0)->length; + memcpy (szUserName, + krb5_princ_component(k5_context, cred.client, 0)->data, + len); + szUserName[len] = '\0'; + } + + + if (r = krb5_get_credentials(k5_context, krb5_kdc_default_options, + ccache, &cred, &new_cred)) { + com_err (NULL, r, "while authorizing."); + krb5_free_cred_contents(k5_context, &cred); + return(0); + } + + ap_opts = 0; + if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) + ap_opts = AP_OPTS_MUTUAL_REQUIRED; + + r = krb5_mk_req_extended(k5_context, &auth_context, ap_opts, + NULL, new_cred, &auth); + + krb5_free_cred_contents(k5_context, &cred); + krb5_free_creds(k5_context, new_cred); + + if (r) { + com_err (NULL, r, "while authorizing."); + return(0); + } + + return(1); +} +/*+*/ +int +kerberos5_reply (int how, unsigned char *data, int cnt) { + static int mutual_complete = 0; + + data += 4; /* Point to status byte */ + + switch (*data++) { + case K5_REJECT: + if (cnt > 0) + wsprintf (strTmp, + "Kerberos V5 refuses authentication because %.*s", + cnt, data); + else + wsprintf (strTmp, "Kerberos V5 refuses authentication"); + MessageBox (HWND_DESKTOP, strTmp, "", MB_OK | MB_ICONEXCLAMATION); + + return KFAILURE; + + case K5_ACCEPT: + if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !mutual_complete) { + wsprintf(strTmp, "Kerberos V5 accepted you, " + "but didn't provide mutual authentication"); + MessageBox (HWND_DESKTOP, strTmp, "", MB_OK | MB_ICONEXCLAMATION); + return KSUCCESS; + } + + return KSUCCESS; + break; + + case K5_RESPONSE: + if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { + /* the rest of the reply should contain a krb_ap_rep */ + krb5_ap_rep_enc_part *reply; + krb5_data inbuf; + krb5_error_code r; + + inbuf.length = cnt; + inbuf.data = (char *)data; + + if (r = krb5_rd_rep (k5_context, auth_context, &inbuf, &reply)) { + com_err (NULL, r, "while authorizing."); + return KFAILURE; + } + krb5_free_ap_rep_enc_part(k5_context, reply); + + mutual_complete = 1; + } + return KSUCCESS; + + default: + return KSUCCESS; // Unknown code + } +} +#endif /* KRB5 */ |