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