aboutsummaryrefslogtreecommitdiffstats
path: root/cram.c
blob: 969d3d903e947aa49d3bb748172e6e62c3569d7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 * cram.c -- CRAM-MD5 authentication (see RFC 2195)
 *
 * For license terms, see the file COPYING in this directory.
 */

#include  "config.h"
#include  <stdio.h>
#include  <string.h>
#include  <ctype.h>
#if defined(STDC_HEADERS)
#include  <stdlib.h>
#endif
#include  "fetchmail.h"
#include  "socket.h"

#include  "i18n.h"
#include  "fm_md5.h"

void hmac_md5 (const unsigned char *password,  size_t pass_len,
               const unsigned char *challenge, size_t chal_len,
               unsigned char *response,  size_t resp_len)
{
    int i;
    unsigned char ipad[64];
    unsigned char opad[64];
    unsigned char hash_passwd[16];

    MD5_CTX ctx;
    
    if (resp_len != 16)
        return;

    if (pass_len > sizeof (ipad))
    {
        MD5Init (&ctx);
        MD5Update (&ctx, password, pass_len);
        MD5Final (hash_passwd, &ctx);
        password = hash_passwd; pass_len = sizeof (hash_passwd);
    }

    memset (ipad, 0, sizeof (ipad));
    memset (opad, 0, sizeof (opad));
    memcpy (ipad, password, pass_len);
    memcpy (opad, password, pass_len);

    for (i=0; i<64; i++) {
        ipad[i] ^= 0x36;
        opad[i] ^= 0x5c;
    }

    MD5Init (&ctx);
    MD5Update (&ctx, ipad, sizeof (ipad));
    MD5Update (&ctx, challenge, chal_len);
    MD5Final (response, &ctx);

    MD5Init (&ctx);
    MD5Update (&ctx, opad, sizeof (opad));
    MD5Update (&ctx, response, resp_len);
    MD5Final (response, &ctx);
}

int do_cram_md5 (int sock, const char *command, struct query *ctl, const char *strip)
/* authenticate as per RFC2195 */
{
    int result;
    int len;
    char buf1[1024];
    char msg_id[768];
    unsigned char response[16];
    char reply[1024];
    char *respdata;

    gen_send (sock, "%s CRAM-MD5", command);

    /* From RFC2195:
     * The data encoded in the first ready response contains an
     * presumptively arbitrary string of random digits, a timestamp, and the
     * fully-qualified primary host name of the server.  The syntax of the
     * unencoded form must correspond to that of an RFC 822 'msg-id'
     * [RFC822] as described in [POP3].
     */

    if ((result = gen_recv (sock, buf1, sizeof (buf1)))) {
	return result;
    }

    /* caller may specify a response prefix we should strip if present */
    respdata = buf1;
    if (strip && strncmp(buf1, strip, strlen(strip)) == 0)
	respdata += strlen(strip);
    len = from64tobits (msg_id, respdata, sizeof(msg_id));

    if (len < 0) {
	report (stderr, GT_("could not decode BASE64 challenge\n"));
	return PS_AUTHFAIL;
    } else if ((size_t)len < sizeof (msg_id)) {
        msg_id[len] = 0;
    } else {
        msg_id[sizeof (msg_id)-1] = 0;
    }
    if (outlevel >= O_DEBUG) {
        report (stdout, GT_("decoded as %s\n"), msg_id);
    }

    /* The client makes note of the data and then responds with a string
     * consisting of the user name, a space, and a 'digest'.  The latter is
     * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where
     * the key is a shared secret and the digested text is the timestamp
     * (including angle-brackets).
     */

    hmac_md5((unsigned char *)ctl->password, strlen(ctl->password),
              (unsigned char *)msg_id, strlen (msg_id),
              response, sizeof (response));

    snprintf (reply, sizeof(reply),
              "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 
              ctl->remotename,
              response[0], response[1], response[2], response[3],
              response[4], response[5], response[6], response[7],
              response[8], response[9], response[10], response[11],
              response[12], response[13], response[14], response[15]);

    to64frombits (buf1, reply, strlen(reply), sizeof buf1);

    /* ship the authentication back, accept the server's responses */
    /* PMDF5.2 IMAP has a bug that requires this to be a single write */
    suppress_tags = TRUE;
    result = gen_transact(sock, "%s", buf1);
    suppress_tags = FALSE;
    if (result)
	return(result);
    else
	return(PS_SUCCESS);
}

/* cram.c ends here */
w"> error (0, 0, "%s (log message incomplete)", partial_message); } #if defined(HAVE_SYSLOG) if (use_syslog) { int priority; #ifdef VA_START VA_START (args, message); #endif priority = status? LOG_ALERT : errnum? LOG_ERR : LOG_INFO; if (errnum > 0) { char *msg = alloca (strlen (message) + 5); strcpy (msg, message); strcat (msg, ": %m"); errno = errnum; #ifdef HAVE_VSYSLOG vsyslog (priority, msg, args); #else { char *a1 = va_arg(args, char *); char *a2 = va_arg(args, char *); char *a3 = va_arg(args, char *); char *a4 = va_arg(args, char *); char *a5 = va_arg(args, char *); char *a6 = va_arg(args, char *); char *a7 = va_arg(args, char *); char *a8 = va_arg(args, char *); syslog (priority, msg, a1, a2, a3, a4, a5, a6, a7, a8); } #endif } else { #ifdef HAVE_VSYSLOG vsyslog (priority, message, args); #else { char *a1 = va_arg(args, char *); char *a2 = va_arg(args, char *); char *a3 = va_arg(args, char *); char *a4 = va_arg(args, char *); char *a5 = va_arg(args, char *); char *a6 = va_arg(args, char *); char *a7 = va_arg(args, char *); char *a8 = va_arg(args, char *); syslog (priority, message, a1, a2, a3, a4, a5, a6, a7, a8); } #endif } #ifdef VA_START va_end(args); #endif } else #endif { if (error_print_progname) (*error_print_progname) (); else { fflush (stdout); if ( *message == '\n' ) { fputc( '\n', stderr ); ++message; } fprintf (stderr, "%s: ", program_name); } #ifdef VA_START VA_START (args, message); # if HAVE_VPRINTF || _LIBC vfprintf (stderr, message, args); # else _doprnt (message, args, stderr); # endif va_end (args); #else fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); #endif if (errnum) { char *tmps = strerror(errnum); if (tmps) { fprintf (stderr, ": %s", tmps); } else { fprintf (stderr, ": Error %d", errnum); } } putc ('\n', stderr); fflush (stderr); } ++error_message_count; if (status) exit (status); } /* * Calling error_init(TRUE) causes error_build and error_complete to write * to stderr without buffering. This is needed for the ticker dots to * work correctly. */ void error_init(foreground) int foreground; { use_stderr = foreground; } /* Build an error message by appending MESSAGE, which is a printf-style format string with optional args, to the existing error message (which may be empty.) The completed error message is finally printed (and reset to empty) by calling error_complete(). If an intervening call to error() occurs when a partially constructed message exists, then, in an attempt to keep the messages in their proper sequence, the partial message will be printed as-is (with a trailing newline) before error() prints its message. */ /* VARARGS */ void #if defined(VA_START) && __STDC__ error_build (const char *message, ...) #else error_build (message, va_alist) const char *message; va_dcl #endif { #ifdef VA_START va_list args; int n; #endif /* Make an initial guess for the size of any single message fragment. */ if (partial_message_size == 0) { partial_message_size_used = 0; partial_message_size = 2048; partial_message = xmalloc (partial_message_size); } else if (partial_message_size - partial_message_size_used < 1024) { partial_message_size += 2048; partial_message = xrealloc (partial_message, partial_message_size); } #if defined(VA_START) VA_START (args, message); #if HAVE_VSNPRINTF || _LIBC for ( ; ; ) { n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used, message, args); if (n < partial_message_size - partial_message_size_used) { partial_message_size_used += n; break; } partial_message_size += 2048; partial_message = xrealloc (partial_message, partial_message_size); } #else vsprintf (partial_message + partial_message_size_used, message, args); partial_message_size_used += strlen(partial_message+partial_message_size_used); /* Attempt to catch memory overwrites... */ if (partial_message_size_used >= partial_message_size) { partial_message_size_used = 0; error (PS_UNDEFINED, 0, "partial error message buffer overflow"); } #endif va_end (args); #else #if HAVE_SNPRINTF for ( ; ; ) { n = snprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8); if (n < partial_message_size - partial_message_size_used) { partial_message_size_used += n; break; } partial_message_size += 2048; partial_message = xrealloc (partial_message, partial_message_size); } #else sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8); /* Attempt to catch memory overwrites... */ if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size) { partial_message_size_used = 0; error (PS_UNDEFINED, 0, "partial error message buffer overflow"); } #endif #endif if (use_stderr && partial_message_size_used != 0) { partial_message_size_used = 0; fputs(partial_message, stderr); } } /* Complete an error message by appending MESSAGE, which is a printf-style format string with optional args, to the existing error message (which may be empty.) The completed error message is then printed (and reset to empty.) */ /* VARARGS */ void #if defined(VA_START) && __STDC__ error_complete (int status, int errnum, const char *message, ...) #else error_complete (status, errnum, message, va_alist) int status; int errnum; const char *message; va_dcl #endif { #ifdef VA_START va_list args; int n; #endif /* Make an initial guess for the size of any single message fragment. */ if (partial_message_size == 0) { partial_message_size_used = 0; partial_message_size = 2048; partial_message = xmalloc (partial_message_size); } else if (partial_message_size - partial_message_size_used < 1024) { partial_message_size += 2048; partial_message = xrealloc (partial_message, partial_message_size); } #if defined(VA_START) VA_START (args, message); #if HAVE_VSNPRINTF || _LIBC for ( ; ; ) { n = vsnprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used, message, args); if (n < partial_message_size - partial_message_size_used) { partial_message_size_used += n; break; } partial_message_size += 2048; partial_message = xrealloc (partial_message, partial_message_size); } #else vsprintf (partial_message + partial_message_size_used, message, args); partial_message_size_used += strlen(partial_message+partial_message_size_used); /* Attempt to catch memory overwrites... */ if (partial_message_size_used >= partial_message_size) { partial_message_size_used = 0; error (PS_UNDEFINED, 0, "partial error message buffer overflow"); } #endif va_end (args); #else #if HAVE_SNPRINTF for ( ; ; ) { n = snprintf (partial_message + partial_message_size_used, partial_message_size - partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8); if (n < partial_message_size - partial_message_size_used) { partial_message_size_used += n; break; } partial_message_size += 2048; partial_message = xrealloc (partial_message, partial_message_size); } #else sprintf (partial_message + partial_message_size_used, message, a1, a2, a3, a4, a5, a6, a7, a8); /* Attempt to catch memory overwrites... */ if ((partial_message_size_used = strlen (partial_message)) >= partial_message_size) { partial_message_size_used = 0; error (PS_UNDEFINED, 0, "partial error message buffer overflow"); } #endif #endif /* Finally... print it. */ partial_message_size_used = 0; if (use_stderr) { fputs(partial_message, stderr); if (errnum) fprintf (stderr, ": %s", strerror (errnum)); putc ('\n', stderr); fflush (stderr); ++error_message_count; if (status) exit(status); } else error (status, errnum, "%s", partial_message); } /* Sometimes we want to have at most one error per line. This variable controls whether this mode is selected or not. */ int error_one_per_line; void #if defined(VA_START) && __STDC__ error_at_line (int status, int errnum, const char *file_name, unsigned int line_number, const char *message, ...) #else error_at_line (status, errnum, file_name, line_number, message, va_alist) int status; int errnum; const char *file_name; unsigned int line_number; const char *message; va_dcl #endif { #ifdef VA_START va_list args; #endif if (error_one_per_line) { static const char *old_file_name; static unsigned int old_line_number; if (old_line_number == line_number && (file_name == old_file_name || !strcmp (old_file_name, file_name))) /* Simply return and print nothing. */ return; old_file_name = file_name; old_line_number = line_number; } if (error_print_progname) (*error_print_progname) (); else { fflush (stdout); if ( *message == '\n' ) { fputc( '\n', stderr ); ++message; } fprintf (stderr, "%s:", program_name); } if (file_name != NULL) fprintf (stderr, "%s:%d: ", file_name, line_number); #ifdef VA_START VA_START (args, message); # if HAVE_VPRINTF || _LIBC vfprintf (stderr, message, args); # else _doprnt (message, args, stderr); # endif va_end (args); #else fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8); #endif ++error_message_count; if (errnum) fprintf (stderr, ": %s", strerror (errnum)); putc ('\n', stderr); fflush (stderr); if (status) exit (status); }