1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2005, 2008, 2009, 2011, 2012,
3 * 2013, 2019
4 * Inferno Nettverk A/S, Norway. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. The above copyright notice, this list of conditions and the following
10 * disclaimer must appear in all copies of the software, derivative works
11 * or modified versions, and any portions thereof, aswell as in all
12 * supporting documentation.
13 * 2. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by
16 * Inferno Nettverk A/S, Norway.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Inferno Nettverk A/S requests users of this software to return to
32 *
33 * Software Distribution Coordinator or sdc@inet.no
34 * Inferno Nettverk A/S
35 * Oslo Research Park
36 * Gaustadall�en 21
37 * NO-0349 Oslo
38 * Norway
39 *
40 * any improvements or extensions that they make and grant Inferno Nettverk A/S
41 * the rights to redistribute these changes.
42 *
43 */
44
45 #include "common.h"
46
47 static const char rcsid[] =
48 "$Id: authneg.c,v 1.128.10.2 2020/11/11 16:11:52 karls Exp $";
49
50 int
negotiate_method(s,packet,route,emsg,emsglen)51 negotiate_method(s, packet, route, emsg, emsglen)
52 int s;
53 socks_t *packet;
54 route_t *route;
55 char *emsg;
56 const size_t emsglen;
57 {
58 const char *function = "negotiate_method()";
59 ssize_t rc;
60 size_t i, requestlen;
61 unsigned char *name = NULL, *password = NULL;
62 unsigned char request[ 1 /* version */
63 + 1 /* number of methods to offer. */
64 + METHODS_KNOWN /* methods we offer server. */
65 ];
66 unsigned char response[ 1 /* version. */
67 + 1 /* method selected by server. */
68 ];
69 char buf[256], lemsg[512] /* local emesg. */;
70 int intmethodv[METHODS_KNOWN];
71
72 if (sockscf.option.debug)
73 slog(LOG_DEBUG, "%s: fd %d, %s", function, s, socket2string(s, NULL, 0));
74
75 #if !SOCKS_CLIENT && HAVE_GSSAPI
76
77 switch (packet->req.auth->method) {
78 case AUTHMETHOD_GSSAPI:
79 /*
80 * Nothing from gssapistate with client we are currently
81 * offering upstream proxyserver, so reset authmethod to none
82 * so as to not confuse things and make any part of the code
83 * try to think this gssapi state relates to the upstream
84 * proxy.
85 */
86 bzero(&packet->req.auth->mdata.gssapi,
87 sizeof(packet->req.auth->mdata.gssapi));
88
89 packet->req.auth->method = AUTHMETHOD_NOTSET;
90 break;
91 }
92 #endif /* !SOCKS_CLIENT && HAVE_GSSAPI */
93
94
95 if (packet->req.version == PROXY_SOCKS_V4) {
96 slog(LOG_DEBUG,
97 "%s: no method negotiate in %s. Setting authmethod to %s",
98 function,
99 proxyprotocol2string(packet->req.version),
100 method2string(AUTHMETHOD_NONE));
101
102
103 packet->req.auth->method = AUTHMETHOD_NONE;
104 packet->res.auth->method = AUTHMETHOD_NONE;
105
106 return 0;
107 }
108
109 SASSERTX(packet->gw.state.smethodc > 0);
110 SASSERTX(packet->gw.state.smethodc <= METHODS_KNOWN);
111
112 /*
113 * create request packet.
114 * version numberOfmethods methods ...
115 */
116
117 requestlen = 0;
118 request[requestlen++] = packet->req.version;
119
120 SASSERTX(requestlen == AUTH_NMETHODS);
121 request[requestlen++] = (unsigned char)0;
122 SASSERTX(request[AUTH_NMETHODS] == 0);
123
124 /*
125 * Count and add the methods we support and are configured to offer
126 * this server.
127 */
128 for (i = 0; i < packet->gw.state.smethodc; ++i) {
129 if (packet->req.auth->method != AUTHMETHOD_NOTSET) {
130 /*
131 * Must be doing serverchaining. Not all methods we are
132 * configured to support for this route may be supported
133 * for this particular client. E.g., if the client has
134 * not provided us with a username/password, we can not
135 * provide the upstream proxy with it either, so don't
136 * offer it.
137 */
138
139 SASSERTX(!SOCKS_CLIENT);
140
141 switch (packet->gw.state.smethodv[i]) {
142 case AUTHMETHOD_NONE:
143 break; /* ok. */
144
145 case AUTHMETHOD_UNAME:
146 if (packet->req.auth->method != AUTHMETHOD_UNAME)
147 continue; /* don't offer this method. */
148
149 break;
150
151 case AUTHMETHOD_GSSAPI:
152 break; /*
153 * ok? Can't forward gssapi/kerberos credentials,
154 * but operator should be able to set up a
155 * things so we can initiate our own gssapi
156 * session to the upsteam proxy.
157 */
158
159 default:
160 SERRX(packet->gw.state.smethodv[i]);
161 }
162 }
163
164 request[requestlen++] = (unsigned char)packet->gw.state.smethodv[i];
165 ++request[AUTH_NMETHODS];
166 }
167
168 SASSERTX(request[AUTH_NMETHODS] > 0);
169 SASSERTX(request[AUTH_NMETHODS] <= METHODS_KNOWN);
170 SASSERTX(request[AUTH_NMETHODS] <= ELEMENTS(intmethodv));
171
172 charmethod2intmethod((ssize_t)request[AUTH_NMETHODS],
173 &request[AUTH_FIRSTMETHOD],
174 intmethodv);
175
176 slog(LOG_NEGOTIATE, "%s: offering proxy server #%d method%s: %s",
177 function,
178 request[AUTH_NMETHODS],
179 request[AUTH_NMETHODS] == 1 ? "" : "s",
180 methods2string(request[AUTH_NMETHODS], intmethodv, buf, sizeof(buf)));
181
182 if (socks_sendton(s,
183 request,
184 requestlen,
185 requestlen,
186 0,
187 NULL,
188 0,
189 NULL,
190 NULL) != (ssize_t)requestlen) {
191 snprintf(emsg, emsglen,
192 "could not offer list of auth methods to proxy server: "
193 "send failed: %s",
194 strerror(errno));
195
196 socks_blacklist(route, emsg);
197 return -1;
198 }
199
200 if ((rc = socks_recvfromn(s,
201 response,
202 sizeof(response),
203 sizeof(response),
204 0,
205 NULL,
206 NULL,
207 NULL,
208 NULL)) != sizeof(response)) {
209 snprintf(emsg, emsglen,
210 "could not read proxy server's response concerning method to "
211 "use, read %ld/%lu: %s",
212 (long)rc,
213 (unsigned long)sizeof(response),
214 rc == 0 ? "server closed connection" : strerror(errno));
215
216 socks_blacklist(route, emsg);
217 return -1;
218 }
219
220 /*
221 * sanity check server's reply.
222 */
223
224 SASSERTX(AUTH_VERSION <= rc);
225 if (request[AUTH_VERSION] != response[AUTH_VERSION]) {
226 snprintf(emsg, emsglen,
227 "got reply version %d from proxy server, but expected version "
228 "%d. Remote proxy server problem?",
229 response[AUTH_VERSION], request[AUTH_VERSION]);
230
231 socks_blacklist(route, emsg);
232 return -1;
233 }
234 packet->version = request[AUTH_VERSION];
235
236 SASSERTX(AUTH_SELECTEDMETHOD <= rc);
237 if (!methodisset(response[AUTH_SELECTEDMETHOD],
238 intmethodv,
239 request[AUTH_NMETHODS])) {
240 if (response[AUTH_SELECTEDMETHOD] == AUTHMETHOD_NOACCEPT)
241 snprintf(emsg, emsglen,
242 "proxy server said we did not offer any acceptable "
243 "authentication methods");
244 else {
245 snprintf(emsg, emsglen,
246 "proxy server selected method 0x%x (%s), but that is not "
247 "among the methods we offered it",
248 response[AUTH_SELECTEDMETHOD],
249 method2string(response[AUTH_SELECTEDMETHOD]));
250
251 swarnx("%s: %s", function, emsg);
252 }
253
254 socks_blacklist(route, emsg);
255 return -1;
256 }
257
258 slog(LOG_NEGOTIATE, "%s: proxy server selected method %s",
259 function, method2string(response[AUTH_SELECTEDMETHOD]));
260
261 switch (response[AUTH_SELECTEDMETHOD]) {
262 case AUTHMETHOD_NONE:
263 rc = 0;
264 break;
265
266 #if HAVE_GSSAPI
267 case AUTHMETHOD_GSSAPI:
268 if (clientmethod_gssapi(s,
269 packet->req.protocol,
270 &packet->gw,
271 packet->req.version,
272 packet->req.auth,
273 lemsg,
274 sizeof(lemsg)) == 0)
275 rc = 0;
276 else
277 rc = -1;
278 break;
279 #endif /* HAVE_GSSAPI */
280
281 case AUTHMETHOD_UNAME:
282 if (clientmethod_uname(s,
283 &packet->gw.addr,
284 packet->req.version,
285 name,
286 password,
287 lemsg,
288 sizeof(lemsg)) == 0)
289 rc = 0;
290 else
291 rc = -1;
292 break;
293
294 case AUTHMETHOD_NOACCEPT:
295 snprintf(lemsg, sizeof(lemsg),
296 "proxy server did not accept any of the authentication "
297 "methods we offered it");
298
299 socks_blacklist(route, emsg);
300 rc = -1;
301 break;
302
303 default:
304 SERRX(packet->req.auth->method);
305 }
306
307 packet->req.auth->method = response[AUTH_SELECTEDMETHOD];
308
309 if (rc == 0) {
310 slog(LOG_NEGOTIATE, "%s: established v%d connection using method %d",
311 function, packet->version, packet->req.auth->method);
312
313 errno = 0; /* all is ok. */
314 }
315 else {
316 snprintf(emsg, emsglen,
317 "failed to establish v%d connection using method %d: %s",
318 packet->version, packet->req.auth->method, lemsg);
319
320 slog(LOG_DEBUG, "%s: %s", function, emsg);
321 }
322
323 return (int)rc;
324 }
325