Line data Source code
1 : #include "../burp.h"
2 : #include "../alloc.h"
3 : #include "../asfd.h"
4 : #include "../async.h"
5 : #include "../cmd.h"
6 : #include "../conf.h"
7 : #include "../conffile.h"
8 : #include "../handy.h"
9 : #include "../iobuf.h"
10 : #include "../log.h"
11 : #include "auth.h"
12 :
13 : #include <time.h>
14 : #include <assert.h>
15 : #include <openssl/rand.h>
16 :
17 : #ifndef UTEST
18 : static
19 : #endif
20 12 : int compare_password(const char *secret, const char *client_supplied)
21 : {
22 : int ret, status;
23 :
24 12 : status = strcmp(secret, client_supplied);
25 :
26 : // To prevent timing attacks passwords a random sleep is inserted when
27 : // the strings didn't match.
28 : //
29 : // Normally a constant-length string comparison would be preferred.
30 : // That doesn't work well here because the length of the secret value
31 : // (the configured password) is not known until measured which could
32 : // leak the length.
33 12 : if (status != 0) {
34 4 : unsigned char delay_bytes[4] = {0};
35 4 : uint32_t delay_nsec = 999999999;
36 :
37 : assert(sizeof(delay_bytes) == sizeof(delay_nsec));
38 :
39 4 : if (RAND_bytes(delay_bytes, sizeof(delay_bytes)) != 1) {
40 0 : unsigned long err = ERR_get_error();
41 0 : logp("RAND_bytes failed: %s\n", ERR_error_string(err, NULL));
42 : // Keep going without random delay
43 : } else {
44 4 : memcpy(&delay_nsec, delay_bytes, sizeof(delay_nsec));
45 : }
46 :
47 4 : struct timespec req = {0};
48 :
49 : // Biased, but good enough for the purpose (the random number
50 : // is not what's important)
51 4 : req.tv_nsec = delay_nsec % 1000000000;
52 :
53 4 : ret = nanosleep(&req, NULL);
54 4 : if (ret) {
55 0 : logp("nanosleep failed with return value %d: %s\n",
56 0 : ret, strerror(errno));
57 0 : return -1;
58 : }
59 : }
60 :
61 : return status;
62 : }
63 :
64 : #ifndef UTEST
65 : static
66 : #endif
67 8 : int check_passwd(const char *passwd, const char *plain_text)
68 : {
69 : #ifndef HAVE_OPENBSD_OS
70 : #ifdef HAVE_CRYPT
71 8 : const char *encrypted=NULL;
72 8 : if(!plain_text || !passwd || strlen(passwd)<13)
73 : return 0;
74 :
75 5 : encrypted=crypt(plain_text, passwd);
76 5 : if (encrypted == NULL) {
77 0 : logp("crypt function failed: %s\n", strerror(errno));
78 0 : return -1;
79 : }
80 :
81 5 : return !compare_password(passwd, encrypted);
82 : #endif
83 : #endif
84 : logp("Server compiled without crypt support - cannot use passwd option\n");
85 : return -1;
86 : }
87 :
88 11 : static int check_client_and_password(struct conf **globalcs,
89 : const char *password, struct conf **cconfs)
90 : {
91 : const char *cname;
92 : int password_check;
93 : // Cannot load it until here, because we need to have the name of the
94 : // client.
95 11 : if(conf_load_clientconfdir(globalcs, cconfs)) return -1;
96 :
97 9 : cname=get_string(cconfs[OPT_CNAME]);
98 9 : password_check=get_int(cconfs[OPT_PASSWORD_CHECK]);
99 :
100 9 : if(!get_string(cconfs[OPT_SSL_PEER_CN]))
101 : {
102 9 : logp("ssl_peer_cn unset");
103 9 : if(cname)
104 : {
105 9 : logp("Falling back to using '%s'\n", cname);
106 9 : if(set_string(cconfs[OPT_SSL_PEER_CN], cname))
107 : return -1;
108 : }
109 : }
110 :
111 9 : cname=get_string(cconfs[OPT_CNAME]);
112 :
113 9 : if(password_check)
114 : {
115 9 : const char *conf_passwd=get_string(cconfs[OPT_PASSWD]);
116 9 : const char *conf_password=get_string(cconfs[OPT_PASSWORD]);
117 9 : if(!conf_password && !conf_passwd)
118 : {
119 1 : logp("password rejected for client %s\n", cname);
120 1 : return -1;
121 : }
122 : // check against plain text
123 8 : if(conf_password && compare_password(conf_password, password))
124 : {
125 1 : logp("password rejected for client %s\n", cname);
126 1 : return -1;
127 : }
128 : // check against encypted passwd
129 7 : if(conf_passwd && !check_passwd(conf_passwd, password))
130 : {
131 1 : logp("password rejected for client %s\n", cname);
132 1 : return -1;
133 : }
134 : }
135 :
136 6 : if(!get_strlist(cconfs[OPT_KEEP]))
137 : {
138 1 : logp("%s: you cannot set the keep value for a client to 0!\n",
139 : cname);
140 1 : return -1;
141 : }
142 : return 0;
143 : }
144 :
145 6 : void version_warn(struct asfd *asfd,
146 : struct cntr *cntr, struct conf **cconfs)
147 : {
148 6 : const char *cname=get_string(cconfs[OPT_CNAME]);
149 6 : const char *peer_version=get_string(cconfs[OPT_PEER_VERSION]);
150 6 : if(!peer_version || strcmp(peer_version, PACKAGE_VERSION))
151 : {
152 3 : char msg[256]="";
153 :
154 3 : if(!peer_version || !*peer_version)
155 2 : snprintf(msg, sizeof(msg), "Client '%s' has an unknown version. Please upgrade.", cname?cname:"unknown");
156 : else
157 1 : snprintf(msg, sizeof(msg), "Client '%s' version '%s' does not match server version '%s'. An upgrade is recommended.", cname?cname:"unknown", peer_version, PACKAGE_VERSION);
158 3 : logw(asfd, cntr, "%s\n", msg);
159 : }
160 6 : }
161 :
162 15 : int authorise_server(struct asfd *asfd,
163 : struct conf **globalcs, struct conf **cconfs)
164 : {
165 15 : int ret=-1;
166 15 : char *cp=NULL;
167 15 : char *password=NULL;
168 15 : char *cname=NULL;
169 15 : char whoareyou[256]="";
170 15 : struct iobuf *rbuf=asfd->rbuf;
171 15 : const char *peer_version=NULL;
172 15 : if(asfd->read(asfd))
173 : {
174 1 : logp("unable to read initial message\n");
175 1 : goto end;
176 : }
177 14 : if(rbuf->cmd!=CMD_GEN || strncmp_w(rbuf->buf, "hello"))
178 : {
179 1 : iobuf_log_unexpected(rbuf, __func__);
180 1 : goto end;
181 : }
182 : // String may look like...
183 : // "hello"
184 : // "hello:(version)"
185 : // (version) is a version number
186 13 : if((cp=strchr(rbuf->buf, ':')))
187 : {
188 12 : cp++;
189 12 : if(cp && set_string(cconfs[OPT_PEER_VERSION], cp))
190 : goto end;
191 : }
192 13 : iobuf_free_content(rbuf);
193 :
194 13 : snprintf(whoareyou, sizeof(whoareyou), "whoareyou");
195 13 : peer_version=get_string(cconfs[OPT_PEER_VERSION]);
196 13 : if(peer_version)
197 : {
198 12 : long min_ver=0;
199 12 : long cli_ver=0;
200 12 : if((min_ver=version_to_long("1.3.2"))<0
201 12 : || (cli_ver=version_to_long(peer_version))<0)
202 : return -1;
203 : // Stick the server version on the end of the whoareyou string.
204 : // if the client version is recent enough.
205 12 : if(min_ver<=cli_ver)
206 12 : snprintf(whoareyou, sizeof(whoareyou),
207 : "whoareyou:%s", PACKAGE_VERSION);
208 : }
209 :
210 13 : if(asfd->write_str(asfd, CMD_GEN, whoareyou)
211 12 : || asfd->read(asfd))
212 : {
213 1 : logp("unable to get client name\n");
214 1 : goto end;
215 : }
216 :
217 12 : if(!(cname=strdup_w(rbuf->buf, __func__)))
218 : goto end;
219 12 : if(!get_int(globalcs[OPT_CNAME_FQDN]))
220 1 : strip_fqdn(&cname);
221 12 : if(get_int(globalcs[OPT_CNAME_LOWERCASE]))
222 3 : strlwr(cname);
223 :
224 12 : if(set_string(cconfs[OPT_CNAME], cname))
225 : goto end;
226 12 : iobuf_free_content(rbuf);
227 :
228 12 : if(asfd->write_str(asfd, CMD_GEN, "okpassword")
229 11 : || asfd->read(asfd))
230 : {
231 1 : logp("unable to get password for client %s\n",
232 : get_string(cconfs[OPT_CNAME]));
233 1 : goto end;
234 : }
235 11 : password=rbuf->buf;
236 11 : iobuf_init(rbuf);
237 :
238 11 : if(check_client_and_password(globalcs, password, cconfs))
239 : goto end;
240 :
241 5 : if(get_int(cconfs[OPT_VERSION_WARN]))
242 5 : version_warn(asfd, get_cntr(globalcs), cconfs);
243 :
244 5 : logp("auth ok for: %s%s\n", get_string(cconfs[OPT_CNAME]),
245 5 : get_int(cconfs[OPT_PASSWORD_CHECK])?
246 : "":" (no password needed)");
247 :
248 5 : if(asfd->write_str(asfd, CMD_GEN, "ok"))
249 : goto end;
250 :
251 5 : ret=0;
252 : end:
253 15 : iobuf_free_content(rbuf);
254 15 : free_w(&password);
255 15 : free_w(&cname);
256 15 : return ret;
257 : }
|