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