/* Copyright (c) 1995-1998 by Cisco systems, Inc. Permission to use, copy, modify, and distribute this software for any purpose and without fee is hereby granted, provided that this copyright and permission notice appear on all copies of the software and supporting documentation, the name of Cisco Systems, Inc. not be used in advertising or publicity pertaining to distribution of the program without specific prior permission, and notice be given in supporting documentation that modification, copying and distribution is by permission of Cisco Systems, Inc. Cisco Systems, Inc. makes no representations about the suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "tac_plus.h" #include #include /* for ntohl() */ #include "encrypt.h" #include "md5.h" #include "utils.h" #include "report.h" #include "packet.h" #include "main.h" /* * create_md5_hash(): create an md5 hash of the "session_id", "the user's * key", "the version number", the "sequence number", and an optional * 16 bytes of data (a previously calculated hash). If not present, this * should be NULL pointer. * * Write resulting hash into the array pointed to by "hash". * * The caller must allocate sufficient space for the resulting hash * (which is 16 bytes long). The resulting hash can safely be used as * input to another call to create_md5_hash, as its contents are copied * before the new hash is generated. * * */ static void create_md5_hash TAC_ARGS((int session_id, const char *key, unsigned version, unsigned seq_no, u_char *prev_hash, u_char *hash)); static void create_md5_hash(session_id, key, version, seq_no, prev_hash, hash) int session_id; const char *key; unsigned version; /* promoted "u_char" type */ unsigned seq_no; /* promoted "u_char" type */ u_char *prev_hash; u_char *hash; { u_char *md_stream, *mdp; int md_len; MD5_CTX mdcontext; u_char version_uchar = version; u_char seq_no_uchar = seq_no; md_len = sizeof(session_id) + strlen(key) + sizeof(version_uchar) + sizeof(seq_no_uchar); if (prev_hash) { md_len += MD5_LEN; } mdp = md_stream = (u_char *) tac_malloc(md_len); bcopy(&session_id, mdp, sizeof(session_id)); mdp += sizeof(session_id); bcopy(key, mdp, strlen(key)); mdp += strlen(key); bcopy(&version_uchar, mdp, sizeof(version_uchar)); mdp += sizeof(version_uchar); bcopy(&seq_no_uchar, mdp, sizeof(seq_no_uchar)); mdp += sizeof(seq_no_uchar); if (prev_hash) { bcopy(prev_hash, mdp, MD5_LEN); mdp += MD5_LEN; } MD5Init(&mdcontext); MD5Update(&mdcontext, md_stream, md_len); MD5Final(hash, &mdcontext); free(md_stream); return; } /* * Overwrite input data with en/decrypted version by generating an MD5 hash and * xor'ing data with it. * * When more than 16 bytes of hash is needed, the MD5 hash is performed * again with the same values as before, but with the previous hash value * appended to the MD5 input stream. * * Return 0 on success, -1 on failure. */ int md5_xor TAC_ARGS((HDR *hdr, u_char *data, const char *key)); int md5_xor(hdr, data, key) HDR *hdr; u_char *data; const char *key; { int i, j; u_char hash[MD5_LEN]; /* the md5 hash */ u_char last_hash[MD5_LEN]; /* the last hash we generated */ u_char *prev_hashp = (u_char *) NULL; /* pointer to last created * hash */ int data_len; int session_id; u_char version; u_char seq_no; data_len = ntohl(hdr->datalength); session_id = hdr->session_id; /* always in network order for hashing */ version = hdr->version; seq_no = hdr->seq_no; if (!key) return (0); for (i = 0; i < data_len; i += 16) { create_md5_hash(session_id, key, version, seq_no, prev_hashp, hash); if (debug & DEBUG_MD5_HASH_FLAG) { int k; report(LOG_DEBUG, "hash: session_id=%u, key=%s, version=%d, seq_no=%d", session_id, key, version, seq_no); if (prev_hashp) { report(LOG_DEBUG, "prev_hash:"); for (k = 0; k < MD5_LEN; k++) report(LOG_DEBUG, "0x%x", prev_hashp[k]); } else { report(LOG_DEBUG, "no prev. hash"); } report(LOG_DEBUG, "hash: "); for (k = 0; k < MD5_LEN; k++) report(LOG_DEBUG, "0x%x", hash[k]); } /* debug */ bcopy(hash, last_hash, MD5_LEN); prev_hashp = last_hash; for (j = 0; j < 16; j++) { if ((i + j) >= data_len) { hdr->encryption = (hdr->encryption == TAC_PLUS_CLEAR) ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR; return (0); } if (debug & DEBUG_XOR_FLAG) { report(LOG_DEBUG, "data[%d] = 0x%x, xor'ed with hash[%d] = 0x%x -> 0x%x\n", i + j, data[i + j], j, hash[j], data[i + j] ^ hash[j]); } /* debug */ data[i + j] ^= hash[j]; } } hdr->encryption = (hdr->encryption == TAC_PLUS_CLEAR) ? TAC_PLUS_ENCRYPTED : TAC_PLUS_CLEAR; return (0); }