/** * A client-side 802.1x implementation supporting EAP/TLS * * This code is released under both the GPL version 2 and BSD licenses. * Either license may be used. The respective licenses are found below. * * Copyright (C) 2002 Chris Hessing & Terry Simons * All Rights Reserved * * --- GPL Version 2 License --- * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * --- BSD License --- * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * Maryland at College Park and its contributors. * - Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /******************************************************************* * EAPOL Function implementations for supplicant * * File: eapmschap.c * * Authors: Chris.Hessing@utah.edu, Terry.Simons@utah.edu * *******************************************************************/ /* TODO: Change DES routines to use OpenSSL Return MS-CHAP failure if server isn't authentic Use xlogf instead of printf. Clean up warnings! Look at implementing MPPE for dynamic keying. Break MS-CHAPv2 code into it's own file, to be used with PEAP and TTLS. */ #include #include #include #include #include #include #include #include "eapmschapv2.h" #include "../../configparse.h" #include "dot1x_globals.h" #include "eap.h" #include "userconf.h" #include "logging.h" #include "auth_methods/auth_tools.h" #include "des.h" #ifndef MSCHAP_DEBUG #define MSCHAP_DEBUG 0 #endif char *eapmschap_netid; char *eapmschap_config; struct mschap_vars { char NtResponse[24]; char PeerChallenge[16]; char AuthenticatorChallenge[16]; }; static struct mschap_vars savedvars; int init_eapmschap(char *config, char *netid) { #ifdef MSCHAP_DEBUG xlogf(DEBUG_AUTHTYPES, "(EAPMS-CHAP) Initalized\n"); #endif eapmschap_netid = netid; eapmschap_config = config; return 0; } void ntpasswordhash(char *uni_password, int len, char *out) { EVP_MD_CTX *cntx; char retVal[16]; int i; cntx = (EVP_MD_CTX *)malloc(sizeof(EVP_MD_CTX)); // We should fix this, so that we are using only OpenSSL, rather than other types of hash routines. EVP_DigestInit(cntx, EVP_md4()); EVP_DigestUpdate(cntx, uni_password, len); EVP_DigestFinal(cntx, (char *)&retVal, (int *)&i); if (i != 16) printf("Returned hash wasn't 16! ACK! (It was %d)\n",i); #if MSCHAP_DEBUG printf("Hash : "); for (i=0; i<16; i++) { printf("%02x",(uint8_t)retVal[i]); } #endif memcpy(out, &retVal, 16); } void HashNtPasswordHash(char *inhash, char *outhash) { EVP_MD_CTX cntx; int i; EVP_DigestInit(&cntx, EVP_md4()); EVP_DigestUpdate(&cntx, inhash, 16); EVP_DigestFinal(&cntx, outhash, &i); } void challenge_hash(char *peer_chal, char *auth_chal, char *username, char *chal) { EVP_MD_CTX *cntx; // The context needed for the hashing. char pre_digest[30]; // The originally returned digest. int retLen; cntx = (EVP_MD_CTX *)malloc(sizeof(EVP_MD_CTX)); EVP_DigestInit(cntx, EVP_sha1()); EVP_DigestUpdate(cntx, peer_chal, 16); EVP_DigestUpdate(cntx, auth_chal, 16); EVP_DigestUpdate(cntx, username, strlen(username)); EVP_DigestFinal(cntx, (char *)&pre_digest, &retLen); if (cntx != NULL) free(cntx); memcpy(chal, &pre_digest, 8); #if MSCHAP_DEBUG printf("Challenge Hash : "); print_hex(chal, 8); #endif } // Taken from FreeRADIUS static void parity_key(char * szOut, const char * szIn) { int i; unsigned char cNext = 0; unsigned char cWorking = 0; for (i = 0; i < 7; i++) { /* Shift operator works in place. Copy the char out */ cWorking = szIn[i]; szOut[i] = (cWorking >> i) | cNext | 1; cWorking = szIn[i]; cNext = (cWorking << (7 - i)); } szOut[i] = cNext | 1; } //Taken from FreeRADIUS static void des_encrypt(const char *szClear, const char *szKey, char *szOut) { char szParityKey[9]; unsigned long ulK[16][2]; parity_key(szParityKey, szKey); /* Insert parity bits */ #if MSCHAP_DEBUG printf("Parity Key : "); print_hex(szParityKey, 9); #endif strncpy(szOut, szClear, 8); /* des encrypts in place */ deskey(ulK, (unsigned char *) szParityKey, 0); /* generate keypair */ des(ulK, szOut); /* encrypt */ } //Taken from FreeRADIUS static void mschap(const char *szChallenge, unsigned char * smbPasswd, char *szResponse) { char szMD4[21]; /* initialize hash string */ memset(szMD4, 0, 21); memcpy(szMD4, smbPasswd, 16); /* * * challenge_response takes an 8-byte challenge string and a * 21-byte hash (16-byte hash padded to 21 bytes with zeros) and * returns a 24-byte response in szResponse */ des_encrypt(szChallenge, szMD4, szResponse); des_encrypt(szChallenge, szMD4 + 7, szResponse + 8); des_encrypt(szChallenge, szMD4 + 14, szResponse + 16); } char ctonibble(char cnib) { char retVal=0x00; char testval=0x00; if ((cnib>='0') && (cnib<='9')) { retVal = cnib - '0'; } else { testval = toupper(cnib); if ((testval>='A') && (testval<='F')) { retVal = ((testval - 'A') +10); } else { printf("Error in conversion! (Check ctonibble()) -- %02x\n",testval); } } return retVal; } // Convert an ASCII string to a binary version of it. void process_hex(char *instr, int size, char *outstr) { int i; // Make sure we don't try to convert something that isn't byte aligned. if ((size % 2) != 0) { printf("Hex string isn't an even number of chars!!!\n"); return; } for (i=0;i<=(size/2);i++) { if (instr[i*2] != 0x00) { outstr[i] = (ctonibble(instr[i*2]) << 4) + ctonibble(instr[(i*2)+1]); #if MSCHAP_DEBUG printf("%02x", (uint8_t)outstr[i]); #endif } } #if MSCHAP_DEBUG printf("\n"); #endif } // This routine decodes the MSCHAPv2 success message, and returns it // as a couple of char *'s to be more useful. Return -1 if we were passed // a string that doesn't look like a success string. int decode_success(char *instr, int instr_size, char *authstr, char *msg) { char *temp; int i; // The success string passed in should look like this : // S= M= if (instr[0] != 'S') return -1; // We shouldn't have a return code more than 40 hex digits, but just to be // safe. temp = (char *)malloc(50); i=2; //Start beyond the S= while (instr[i] != ' ') { temp[i-2] = instr[i]; i++; } temp[i-2] = 0x00; // Make it so we can print the string correctly. #if MSCHAP_DEBUG printf("Processing string : %s\n", temp); #endif process_hex(temp, i-2, authstr); free(temp); #if MSCHAP_DEBUG printf("Returned : "); print_hex(authstr, 20); // 40 chars, boils down to 20 bytes. printf("\n"); #endif // Skip to the next character. while (instr[i] != ' ') i++; // Make sure we have a message here. if (instr[i] != 'M') return -1; i+=2; // Skip to the first character in the message. memcpy(msg, &instr[i], (instr_size - i)); return 0; } void decode_error(char *instr, int *err, int *retry, char *challenge, int *pchange, char *msg) { char *err_blk=NULL, *retry_blk=NULL, *chal_blk=NULL, *pchange_blk=NULL; char *msg_blk = NULL; char *junk, *temp_store; if (instr[0] != 'E') // Then we don't have an error. { printf("The returned message isn't formatted correctly!\n"); return; } sprintf(instr, "%s %s %s %s %s", err_blk, retry_blk, chal_blk, pchange_blk, msg_blk); // Now, process each block. err = (int)strtod(&err_blk[2], &junk); #if MSCHAP_DEBUG printf("Error number : %d\n", err); #endif retry = (int)strtod(&retry_blk[2], &junk); #if MSCHAP_DEBUG printf("Retry value : %d\n", retry); #endif temp_store = (char *)malloc(32); // It shouldn't be more than 16 bytes. memcpy(temp_store, &chal_blk[2], 32); process_hex(temp_store, 32, challenge); free(temp_store); #if MSCHAP_DEBUG printf("Challenge Value : "); print_hex(challenge, 16); #endif pchange = (int)strtod(&pchange_blk[2], &junk); #if MSCHAP_DEBUG printf("Password change value (should be 3) : %d\n", pchange); #endif memcpy(msg, &msg_blk[2], (strlen(msg_blk)-2)); #if MSCHAP_DEBUG printf("Result message : %s\n", msg); #endif } // Convert a string to unicode(ish) char *to_uni(char *non_uni) { char *retUni; int i; retUni = (char *)malloc((strlen(non_uni)+1)*2); bzero(retUni, ((strlen(non_uni)+1)*2)); for (i=0; i found in the config file. ******************************************************************/ int eapmschap_auth_challenge() { char *temp_username=NULL; char *temp_password=NULL; char *trash; // We may want to ask for the user's password. temp_username = get_username(); // We need to think of a better way to handle this. Right now, if you // put in an incorrect password, you can never fix it. trash = get_password(); if (trash == NULL) { xlogf(DEBUG_NORMAL, "(MS-CHAPv2 Authentication) %s's Password : ",temp_username); temp_password = getpass(""); //This is obsolete, fix it! set_password(temp_password); // Update our config values. } else { xlogf(DEBUG_AUTHTYPES, "get_password returned a value!\n"); } free(trash); free(temp_username); temp_username = NULL; return 0; } // Clean up any memory we have allocated, and destroy anything we need to. int eapmschap_shutdown() { // Don't free one_x_globals as it is a copy of a pointer to memory, and // the shutdown_eap function will free it. #ifdef MSCHAP_DEBUG xlogf(DEBUG_AUTHTYPES, "(EAPMS-CHAP) Cleaning up.\n"); #endif return 0; }