]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/cram-md5.c
Backport from BEE
[bacula/bacula] / bacula / src / lib / cram-md5.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2001-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *  Challenge Response Authentication Method using MD5 (CRAM-MD5)
18  *
19  * cram-md5 is based on RFC2104.
20  *
21  * Written for Bacula by Kern E. Sibbald, May MMI.
22  *
23  */
24
25 #include "bacula.h"
26
27 const int dbglvl = 50;
28
29 /* Authorize other end
30  * Codes that tls_local_need and tls_remote_need can take:
31  *   BNET_TLS_NONE     I cannot do tls
32  *   BNET_TLS_OK       I can do tls, but it is not required on my end
33  *   BNET_TLS_REQUIRED  tls is required on my end
34  *
35  *   Returns: false if authentication failed
36  *            true if OK
37  */
38 bool cram_md5_challenge(BSOCK *bs, const char *password, int tls_local_need, int compatible)
39 {
40    struct timeval t1;
41    struct timeval t2;
42    struct timezone tz;
43    int i;
44    bool ok;
45    char chal[MAXSTRING];
46    char host[MAXSTRING];
47    uint8_t hmac[20];
48
49    gettimeofday(&t1, &tz);
50    for (i=0; i<4; i++) {
51       gettimeofday(&t2, &tz);
52    }
53    srandom((t1.tv_sec&0xffff) * (t2.tv_usec&0xff));
54    if (!gethostname(host, sizeof(host))) {
55       bstrncpy(host, my_name, sizeof(host));
56    }
57    /* Send challenge -- no hashing yet */
58    bsnprintf(chal, sizeof(chal), "<%u.%u@%s>", (uint32_t)random(), (uint32_t)time(NULL), host);
59    if (compatible) {
60       Dmsg2(dbglvl, "send: auth cram-md5 challenge %s ssl=%d\n", chal, tls_local_need);
61       if (!bs->fsend("auth cram-md5 %s ssl=%d\n", chal, tls_local_need)) {
62          Dmsg1(dbglvl, "Send challenge comm error. ERR=%s\n", bs->bstrerror());
63          return false;
64       }
65    } else {
66       /* Old non-compatible system */
67       Dmsg2(dbglvl, "send: auth cram-md5 challenge %s ssl=%d\n", chal, tls_local_need);
68       if (!bs->fsend("auth cram-md5 %s ssl=%d\n", chal, tls_local_need)) {
69          Dmsg1(dbglvl, "Send challenge comm error. ERR=%s\n", bs->bstrerror());
70          return false;
71       }
72    }
73
74    /* Read hashed response to challenge */
75    if (bs->wait_data(180) <= 0 || bs->recv() <= 0) {
76       Dmsg1(dbglvl, "Receive cram-md5 response comm error. ERR=%s\n", bs->bstrerror());
77       bmicrosleep(5, 0);
78       return false;
79    }
80
81    /* Attempt to duplicate hash with our password */
82    hmac_md5((uint8_t *)chal, strlen(chal), (uint8_t *)password, strlen(password), hmac);
83    bin_to_base64(host, sizeof(host), (char *)hmac, 16, compatible);
84    ok = strcmp(bs->msg, host) == 0;
85    if (ok) {
86       Dmsg1(dbglvl, "Authenticate OK %s\n", host);
87    } else {
88       bin_to_base64(host, sizeof(host), (char *)hmac, 16, false);
89       ok = strcmp(bs->msg, host) == 0;
90       if (!ok) {
91          Dmsg2(dbglvl, "Authenticate NOT OK: wanted %s, got %s\n", host, bs->msg);
92       }
93    }
94    if (ok) {
95       bs->fsend("1000 OK auth\n");
96    } else {
97       bs->fsend(_("1999 Authorization failed.\n"));
98       bmicrosleep(5, 0);
99    }
100    return ok;
101 }
102
103 /* Respond to challenge from other end */
104 bool cram_md5_respond(BSOCK *bs, const char *password, int *tls_remote_need, int *compatible)
105 {
106    char chal[MAXSTRING];
107    uint8_t hmac[20];
108
109    *compatible = false;
110    if (bs->recv() <= 0) {
111       bmicrosleep(5, 0);
112       return false;
113    }
114    if (bs->msglen >= MAXSTRING) {
115       Dmsg1(dbglvl, "Msg too long wanted auth cram... Got: %s", bs->msg);
116       bmicrosleep(5, 0);
117       return false;
118    }
119    Dmsg1(100, "cram-get received: %s", bs->msg);
120    if (sscanf(bs->msg, "auth cram-md5c %s ssl=%d", chal, tls_remote_need) == 2) {
121       *compatible = true;
122    } else if (sscanf(bs->msg, "auth cram-md5 %s ssl=%d", chal, tls_remote_need) != 2) {
123       if (sscanf(bs->msg, "auth cram-md5 %s\n", chal) != 1) {
124          Dmsg1(dbglvl, "Cannot scan received response to challenge: %s", bs->msg);
125          bs->fsend(_("1999 Authorization failed.\n"));
126          bmicrosleep(5, 0);
127          return false;
128       }
129    }
130
131    hmac_md5((uint8_t *)chal, strlen(chal), (uint8_t *)password, strlen(password), hmac);
132    bs->msglen = bin_to_base64(bs->msg, 50, (char *)hmac, 16, *compatible) + 1;
133 // Don't turn the following on except for local debugging -- security
134 // Dmsg3(100, "get_auth: chal=%s pw=%s hmac=%s\n", chal, password, bs->msg);
135    if (!bs->send()) {
136       Dmsg1(dbglvl, "Send challenge failed. ERR=%s\n", bs->bstrerror());
137       return false;
138    }
139    Dmsg1(99, "sending resp to challenge: %s\n", bs->msg);
140    if (bs->wait_data(180) <= 0 || bs->recv() <= 0) {
141       Dmsg1(dbglvl, "Receive cram-md5 response failed. ERR=%s\n", bs->bstrerror());
142       bmicrosleep(5, 0);
143       return false;
144    }
145    if (strcmp(bs->msg, "1000 OK auth\n") == 0) {
146       return true;
147    }
148    Dmsg1(dbglvl, "Received bad response: %s\n", bs->msg);
149    bmicrosleep(5, 0);
150    return false;
151 }