/* * passlog.cpp - basic log formats implementation * $Id: passlog.cpp,v 1.4 2004/06/05 15:15:17 rdenisc Exp $ */ /*********************************************************************** * Copyright (C) 2002-2003 Remi Denis-Courmont. * * This program is free software; you can redistribute and/or modify * * it under the terms of the GNU General Public License as published * * by the Free Software Foundation; version 2 of the license. * * * * 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, you can get it from: * * http://www.gnu.org/copyleft/gpl.html * ***********************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "passlog.h" #include // isspace() #include // sscanf() #include // strchr() /* * Returns a pointer to the first non 'white' (space or tab) char in a string. * CRASHES if str si NULL. */ static const char * eat_white (const char *str) { while (isspace (*str)) str++; return str; } /* * Performs a case-insensitive comparaison of the first word of two strings. * Returns true if different, 0 if equal. * CRASHES if any param is NULL. */ static int wordcasecmp (const char *w1, const char *w2) { w1 = eat_white (w1); w2 = eat_white (w2); do { char c1 = *w1++, c2 = *w2++; if (isspace (c1) || !c1) return !(isspace (c2) || !c2); if (tolower (c1) != tolower (c2)) return 1; } while (1); return 1; // dead code } /* * Performs base64 decoding. */ #define BASE64_SKIP -3 #define BASE64_EOF -2 #define BASE64_ERR -1 static char base64_reduce (char c) { if ((c >= 'A') && (c <= 'Z')) return c - 'A'; if ((c >= 'a') && (c <= 'z')) return c - 'a' + 26; if ((c >= '0') && (c <= '9')) return c - '0' + 52; switch (c) { case '+': return 62; case '/': return 63; case '=': return BASE64_EOF; case '\n': case '\r': case ' ': return BASE64_SKIP; } return BASE64_ERR; } /* * Decodes Base64-encoded source to target. * Returns 0 on success, -1 on I/O error, -2 if source is not Base64-encoded. */ static int unbase64 (unsigned char *dec, const unsigned char *enc, size_t maxlen) { for (size_t len = 0; len < maxlen; len += 3) { unsigned char in[4]; unsigned avail = 4; for (int i = 0; (i < 4) && (avail == 4); i++) { // reads one byte int buf = *enc; if (buf == 0) buf = BASE64_EOF; // end of encoded string else enc++; // decodes a 6-bits value from an octet buf = base64_reduce (buf); switch (buf) { case BASE64_SKIP: /* skips CR/LF/SP */ break; case BASE64_ERR: /* invalid base64 char */ return -1; case BASE64_EOF: /* end of string */ avail = i; break; default: in[i] = (unsigned char)buf; } } if (maxlen <= (len + ((avail > 0) ? (avail - 1) : 0))) return -1; // output buffer too short switch (avail) { case 4: dec[len + 2] = (in[2] << 6) | (in[3] >> 0); case 3: dec[len + 1] = (in[1] << 4) | (in[2] >> 2); case 2: dec[len] = (in[0] << 2) | (in[1] >> 4); } if (avail < 4) return 0; } return 0; } /*** PasswordDataLog class implementation ***/ int PasswordDataLog::WriteServerLine (const char *, int length, int) { #if 0 if (!oob) { /*switch (proto_class) { case undefined: case unknown: case HTTP_like: case POP_like: case FTP_like: }*/ } #endif return length; } /* * There is of course still a clever way to escape password capture * by sending an overly long quantity of spaces between PASS and your actual * password, or by getting your password split into two parts by the * line-bufferization engine. */ int PasswordDataLog::WriteClientLine (const char *data, int length, int oob) { if (!oob) { // FTP/SMTP/NNTP/POP1/POP3: if (!wordcasecmp ("USER", data)) sscanf (data, "%*s %127s", username); else if (!wordcasecmp ("PASS", data)) { if ((sscanf (data, "%*s %127s", password) == 1) && *username) fprintf (out, "UNKNOWN: %s:%s\n", username, password); } else // POP2 (but not SMTP!!): if (!wordcasecmp ("HELO", data)) { if (sscanf (data, "%*s %127s %127s", username, password) == 2) fprintf (out, "UNKNOWN: %s:%s\n", username, password); } else // HTTP Basic authentication if (!wordcasecmp ("Authorization:", data) || !wordcasecmp ("Proxy-Authorization:", data)) { unsigned char buf_enc[345]; char buf_dec[256]; if (sscanf (data, "%*s Basic %511s\n", buf_enc) == 1) { unbase64 ((unsigned char*)buf_dec, buf_enc, sizeof (buf_dec)); char *lim = strchr (buf_dec, ':'); if (lim != NULL) { *lim = 0; lim++; fprintf (out, "UNKNOWN: %s:%s\n", buf_dec, lim); } } } } return length; } DataLog *PasswordLogMaker (void) { return new PasswordDataLog; }