1 /*
2  * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2004, 2005, 2008, 2009, 2010,
3  *               2011, 2012, 2013, 2014
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  /*
46   * The gssapi code was contributed by
47   * Markus Moeller (markus_moeller at compuserve.com).
48   */
49 
50 
51 #include "common.h"
52 
53 #if SOCKS_CLIENT || SOCKS_SERVER /* XXX is this correct? */
54 #include "interposition.h"
55 #endif /* SOCKS_CLIENT || SOCKS_SERVER */
56 
57 static const char rcsid[] =
58 "$Id: clientprotocol.c,v 1.225.4.4.6.1 2021/01/07 15:46:46 karls Exp $";
59 
60 static int
61 recv_sockshost(int s, sockshost_t *host, int version, authmethod_t *auth,
62                char *emsg, const size_t emsglen);
63 /*
64  * Fills "host" based on data read from "s".  "version" is the version
65  * the remote peer is expected to send data in.
66  *
67  * Returns:
68  *      On success: 0
69  *      On failure: -1
70  */
71 
72 int
socks_sendrequest(s,request,emsg,emsglen)73 socks_sendrequest(s, request, emsg, emsglen)
74    int s;
75    const request_t *request;
76    char *emsg;
77    const size_t emsglen;
78 {
79    const char *function = "socks_sendrequest()";
80    ssize_t rc;
81    size_t len;
82    unsigned char requestmem[sizeof(*request)], *p = requestmem;
83 
84    switch (request->version) {
85       case PROXY_SOCKS_V4:
86          /*
87           * VN   CD  DSTPORT DSTIP USERID   0
88           *  1 + 1  +   2   +  4  +  ?    + 1  = 9 + USERID
89           */
90 
91          /* VN */
92          memcpy(p, &request->version, sizeof(request->version));
93          p += sizeof(request->version);
94 
95          /* CD */
96          memcpy(p, &request->command, sizeof(request->command));
97          p += sizeof(request->command);
98 
99          p = sockshost2mem(&request->host, p, request->version);
100 
101          *p = NUL; /* not bothering to send any userid.  Should we? */
102          ++p;
103          break;
104 
105        case PROXY_SOCKS_V5:
106          /*
107           * rfc1928 request:
108           *
109           *   +----+-----+-------+------+----------+----------+
110           *   |VER | CMD |  FLAG | ATYP | DST.ADDR | DST.PORT |
111           *   +----+-----+-------+------+----------+----------+
112           *   | 1  |  1  |   1   |  1   | Variable |    2     |
113           *   +----+-----+-------+------+----------+----------+
114           *     1      1     1      1       > 0         2
115           *
116           *   Which gives a fixed size of minimum 7 octets.
117           *   The first octet of DST.ADDR when it is SOCKS_ADDR_DOMAINNAME
118           *   contains the length of DST.ADDR.
119           */
120 
121          /* VER */
122          memcpy(p, &request->version, sizeof(request->version));
123          p += sizeof(request->version);
124 
125          /* CMD */
126          memcpy(p, &request->command, sizeof(request->command));
127          p += sizeof(request->command);
128 
129          /* FLAG */
130          memcpy(p, &request->flag, sizeof(request->flag));
131          p += sizeof(request->flag);
132 
133          p = sockshost2mem(&request->host, p, request->version);
134          break;
135 
136        default:
137          SERRX(request->version);
138    }
139 
140    slog(LOG_NEGOTIATE, "%s: sending request to server: %s",
141         function, socks_packet2string(request, 1));
142 
143    /*
144     * Send the request to the server.
145     */
146    len = p - requestmem;
147    if ((rc = socks_sendton(s,
148                            requestmem,
149                            len,
150                            len,
151                            0,
152                            NULL,
153                            0,
154                            NULL,
155                            request->auth)) != (ssize_t)len) {
156       snprintf(emsg, emsglen,
157                "could not send request to proxy server.  Sent %ld/%lu: %s",
158                (long)rc, (unsigned long)len, strerror(errno));
159       return -1;
160    }
161 
162    return 0;
163 }
164 
165 int
socks_recvresponse(s,response,version,emsg,emsglen)166 socks_recvresponse(s, response, version, emsg, emsglen)
167    int s;
168    response_t   *response;
169    int version;
170    char *emsg;
171    const size_t emsglen;
172 {
173    const char *function = "socks_recvresponse()";
174    ssize_t rc;
175 
176    /* get the version specific data that prefixes the sockshost. */
177    switch (version) {
178       case PROXY_SOCKS_V4: {
179          /*
180           * The socks V4 reply length is fixed:
181           * VN   CD  DSTPORT  DSTIP
182           *  1 + 1  +   2   +   4
183           */
184          char responsemem[ sizeof(response->version)
185                          + sizeof(response->reply.socks)
186                          ];
187          char *p = responsemem;
188 
189          if ((rc = socks_recvfromn(s,
190                                    responsemem,
191                                    sizeof(responsemem),
192                                    sizeof(responsemem),
193                                    0,
194                                    NULL,
195                                    NULL,
196                                    NULL,
197                                    response->auth))
198          != (ssize_t)sizeof(responsemem)) {
199             fmtresponseerror(rc, sizeof(responsemem), emsg, emsglen);
200             return -1;
201          }
202 
203          /* VN */
204          memcpy(&response->version, p, sizeof(response->version));
205          p += sizeof(response->version);
206          if (response->version != PROXY_SOCKS_V4REPLY_VERSION) {
207             fmtversionerror(PROXY_SOCKS_V4REPLY_VERSION,
208                             response->version,
209                             emsg,
210                             emsglen);
211             return -1;
212          }
213 
214          /* CD */
215          memcpy(&response->reply.socks, p, sizeof(response->reply.socks));
216          p += sizeof(response->reply.socks);
217          break;
218       }
219 
220       case PROXY_SOCKS_V5: {
221          /*
222           * rfc1928 reply:
223           *
224           * +----+-----+-------+------+----------+----------+
225           * |VER | REP |  FLAG | ATYP | BND.ADDR | BND.PORT |
226           * +----+-----+-------+------+----------+----------+
227           * | 1  |  1  |   1   |  1   |  > 0     |    2     |
228           * +----+-----+-------+------+----------+----------+
229           *
230           *   Which gives a size of >= 7 octets.
231           *
232           */
233          char responsemem[sizeof(response->version)
234                         + sizeof(response->reply.socks)
235                         + sizeof(response->flag)
236                         ];
237          char *p = responsemem;
238 
239          if ((rc = socks_recvfromn(s,
240                                    responsemem,
241                                    sizeof(responsemem),
242                                    sizeof(responsemem),
243                                    0,
244                                    NULL,
245                                    NULL,
246                                    NULL,
247                                    response->auth))
248          != (ssize_t)sizeof(responsemem)) {
249             fmtresponseerror(rc, sizeof(responsemem), emsg, emsglen);
250             return -1;
251          }
252 
253          /* VER */
254          memcpy(&response->version, p, sizeof(response->version));
255          p += sizeof(response->version);
256          if (version != response->version) {
257             fmtversionerror(version, response->version, emsg, emsglen);
258             return -1;
259          }
260 
261          /* REP */
262          memcpy(&response->reply.socks, p, sizeof(response->reply.socks));
263          p += sizeof(response->reply.socks);
264 
265          /* FLAG */
266          memcpy(&response->flag, p, sizeof(response->flag));
267          p += sizeof(response->flag);
268 
269          break;
270       }
271 
272       default:
273          SERRX(version);
274    }
275 
276    if (recv_sockshost(s,
277                       &response->host,
278                       version,
279                       response->auth,
280                       emsg,
281                       emsglen) != 0)
282       return -1;
283 
284    slog(LOG_NEGOTIATE, "%s: received response from server: %s",
285         function, socks_packet2string(response, 0));
286 
287    return 0;
288 }
289 
290 int
socks_negotiate(s,control,packet,route,emsg,emsglen)291 socks_negotiate(s, control, packet, route, emsg, emsglen)
292    int s;
293    int control;
294    socks_t *packet;
295    route_t *route;
296    char *emsg;
297    const size_t emsglen;
298 {
299    const char *function = "socks_negotiate()";
300    char sbuf[512], cbuf[512];
301    int failed = 0;
302 
303    slog(LOG_NEGOTIATE,
304         "%s: initiating %s negotiation with control-fd %d (%s), "
305         "data-fd %d (%s), req.host = %s",
306         function,
307         proxyprotocol2string(packet->req.version),
308         control,
309         control == -1 ? "N/A" : socket2string(control, cbuf, sizeof(cbuf)),
310         s,
311         s == control ? "same" : socket2string(s, sbuf, sizeof(sbuf)),
312         sockshost2string(&packet->req.host, NULL, 0));
313 
314    /* avoid false Valgrind warning later due to uninitialized padding bits. */
315    bzero(&packet->res.host, sizeof(packet->res.host));
316 
317    packet->res.auth = packet->req.auth;
318 
319    switch (packet->req.version) {
320       case PROXY_SOCKS_V4:
321          if (packet->req.command == SOCKS_BIND) {
322             if (route != NULL && route->gw.state.extension.bind)
323                packet->req.host.addr.ipv4.s_addr = htonl(BINDEXTENSION_IPADDR);
324 #if SOCKS_CLIENT
325             else {
326                if (packet->req.version == PROXY_SOCKS_V4)
327                    /*
328                     * v4/v5 difference.  We always set up for v5 by default,
329                     * but if v4 is what proxyserver us, modify the request
330                     * slightly for v4.
331                     */
332                   if (ntohs(sockscf.state.lastconnect.port) != 0)
333                      packet->req.host.port = sockscf.state.lastconnect.port;
334             }
335 #endif /* SOCKS_CLIENT */
336          }
337 
338          /* FALLTHROUGH */
339 
340       case PROXY_SOCKS_V5: {
341          /*
342           * Whatever these file descriptor-indexes were used for before, we
343           * need to reset them now.
344           */
345          int rc;
346 
347 #if SOCKS_CLIENT
348          int original_s; /* false gcc warning: may be used uninitialized */
349          int executingdnscode;
350 
351          socks_rmaddr(s, 1);
352          socks_rmaddr(control, 1);
353 
354          /*
355           * Some resolverlibrary have bugs whose side-effect leads to
356           * them either closing the socket they created and called us
357           * on (e.g., for connect(2)), or they (naturally) don't expect
358           * that when they call connect(2), connect(2) will end up
359           * calling them again. The later can happen when their connect(2)
360           * is caught by our Rconnect(), and our Rconnect() needs to
361           * call one of the dns-functions to reach the socks-server.
362           *
363           * We attempt to slightly increase the chances of this working
364           * by dup(2)'ing the socket they called us with so that after
365           * method_negotiate(), which may end up calling dns-functions,
366           * returns, we still have the original socket they called us
367           * with, even if the dns-calls made by method_negotiate() ended
368           * up closing and recreating it.
369           */
370 
371          SASSERTX(sockscf.state.executingdnscode >= 0);
372          if (sockscf.state.executingdnscode
373          &&  s != control
374             /* workaround is only usable for udp. */
375          &&  packet->req.command == SOCKS_UDPASSOCIATE)
376             executingdnscode = 1;
377          else
378             executingdnscode = 0;
379 
380          if (executingdnscode) {
381             slog(LOG_DEBUG,
382                  "%s: preparing to call method_negotiate() from dns-code",
383                  function);
384 
385             if ((original_s = dup(s)) == -1)
386                swarn("%s: dup() failed on fd %d while executing dns-code",
387                      function, s);
388             else {
389                int tmp_s = socketoptdup(s, -1);
390 
391                if (tmp_s == -1)
392                   swarn("%s: socketoptdup() failed on fd %d while executing "
393                         "dns-code",
394                         function, s);
395                else {
396                   rc = dup2(tmp_s, s);
397                   close(tmp_s);
398 
399                   if (rc == s) {
400                      slog(LOG_DEBUG,
401                           "%s: successfully prepared things.  Data-fd %d is "
402                           "now a dummy-fd, while original data-fd is saved as "
403                           "fd %d",
404                           function, s, original_s);
405                   }
406                   else
407                      swarn("%s: dup2() failed on fd %d, fd %d while executing "
408                            "dns-code",
409                            function, tmp_s, s);
410                }
411             }
412          }
413 #endif /* SOCKS_CLIENT */
414 
415          rc = negotiate_method(control, packet, route, emsg, emsglen);
416 
417 #if SOCKS_CLIENT
418          if (executingdnscode && original_s != -1) {
419             const int errno_s = errno;
420 
421             slog(LOG_DEBUG, "%s: restoring data fd %d from saved fd %d (%s)",
422                  function, s, original_s, socket2string(original_s, NULL, 0));
423 
424             if (dup2(original_s, s) != s)
425                swarn("%s: failed to restore data fd %d from saved fd %d",
426                      function, s, original_s);
427 
428             close(original_s);
429             errno = errno_s;
430          }
431 #endif /* SOCKS_CLIENT */
432 
433          if (rc != 0) {
434             if (errno == 0) /* something wrong.  If nothing else ... */
435                errno = ECONNREFUSED;
436 
437             failed = 1;
438             break;
439          }
440 
441          slog(LOG_DEBUG,
442               "%s: method negotiation successful.  Server selected method "
443               "%d (%s)",
444               function,
445               packet->req.auth->method,
446               method2string(packet->req.auth->method));
447 
448          if (socks_sendrequest(control, &packet->req, emsg, emsglen) != 0) {
449             failed = 1;
450             break;
451          }
452 
453          if (socks_recvresponse(control,
454                                 &packet->res,
455                                 packet->req.version,
456                                 emsg,
457                                 emsglen) != 0) {
458             socks_blacklist(route, emsg);
459 
460             if (errno == 0) /* something wrong.  If nothing else ... */
461                errno = ECONNREFUSED;
462 
463             failed = 1;
464          }
465 
466          break;
467       }
468 
469       case PROXY_HTTP_10:
470       case PROXY_HTTP_11:
471          if (httpproxy_negotiate(control, packet, emsg, emsglen) != 0) {
472             if (errno == 0)
473                errno = ECONNREFUSED; /* something wrong.  If nothing else ... */
474 
475             failed = 1;
476          }
477 
478          break;
479 
480 #if HAVE_LIBMINIUPNP
481       case PROXY_UPNP:
482          if (upnp_negotiate(s, packet, &route->gw, emsg, emsglen) != 0) {
483             if (errno == 0)
484                errno = ECONNREFUSED; /* something wrong.  If nothing else ... */
485 
486             failed = 1;
487          }
488 
489          break;
490 #endif /* HAVE_LIBMINIUPNP */
491 
492       default:
493          SERRX(packet->req.version);
494    }
495 
496    if (!failed) {
497       if (serverreplyisok(packet->res.version,
498                           packet->req.command,
499                           socks_get_responsevalue(&packet->res),
500                           route,
501                           emsg,
502                           emsglen)) {
503          if (errno != EINPROGRESS)
504             errno = 0; /* all should be ok. */
505       }
506       else {
507          SASSERTX(errno != 0);
508          failed = 1;
509       }
510    }
511 
512    if (failed) {
513 #if HAVE_GSSAPI
514       if (packet->req.auth->method == AUTHMETHOD_GSSAPI
515       &&  packet->req.auth->mdata.gssapi.state.id != GSS_C_NO_CONTEXT) {
516          OM_uint32 major_status, minor_status;
517          char buf[512];
518 
519          if ((major_status
520          = gss_delete_sec_context(&minor_status,
521                                   &packet->req.auth->mdata.gssapi.state.id,
522                                   GSS_C_NO_BUFFER)) != GSS_S_COMPLETE) {
523             if (!gss_err_isset(major_status, minor_status, buf, sizeof(buf)))
524                *buf = NUL;
525 
526             swarnx("%s: gss_delete_sec_context() failed%s%s",
527                    function,
528                    *buf == NUL ? "" : ": ",
529                    *buf == NUL ? "" : buf);
530          }
531       }
532 #endif /* HAVE_GSSAPI */
533 
534       return -1;
535    }
536 
537    return 0;
538 
539 }
540 
541 #if SOCKS_CLIENT
542 void
update_after_negotiate(packet,socksfd)543 update_after_negotiate(packet, socksfd)
544    const socks_t *packet;
545    socksfd_t *socksfd;
546 {
547 
548    socksfd->state.auth    = *packet->req.auth;
549    socksfd->state.command = packet->req.command;
550    socksfd->state.version = packet->req.version;
551 }
552 
553 #endif /* SOCKS_CLIENT */
554 
555 static int
recv_sockshost(s,host,version,auth,emsg,emsglen)556 recv_sockshost(s, host, version, auth, emsg, emsglen)
557    int s;
558    sockshost_t *host;
559    int version;
560    authmethod_t *auth;
561    char *emsg;
562    const size_t emsglen;
563 {
564    const char *function = "recv_sockshost()";
565    ssize_t rc;
566 
567    switch (version) {
568       case PROXY_SOCKS_V4:
569       case PROXY_SOCKS_V4REPLY_VERSION: {
570          /*
571           * DSTPORT  DSTIP
572           *   2    +   4
573           */
574          char hostmem[ sizeof(host->port)
575                      + sizeof(host->addr.ipv4)
576                      ];
577          char *p = hostmem;
578 
579          if ((rc = socks_recvfromn(s,
580                                    hostmem,
581                                    sizeof(hostmem),
582                                    sizeof(hostmem),
583                                    0,
584                                    NULL,
585                                    NULL,
586                                    NULL,
587                                    auth)) != (ssize_t)sizeof(hostmem)) {
588             fmtresponseerror(rc, sizeof(hostmem), emsg, emsglen);
589             return -1;
590          }
591 
592          host->atype = SOCKS_ADDR_IPV4;
593 
594          /* BND.PORT */
595          memcpy(&host->port, p, sizeof(host->port));
596          p += sizeof(host->port);
597 
598          /* BND.ADDR */
599          memcpy(&host->addr.ipv4, p, sizeof(host->addr.ipv4));
600          p += sizeof(host->addr.ipv4);
601 
602          break;
603       }
604 
605       case PROXY_SOCKS_V5:
606          /*
607           * +------+----------+----------+
608           * | ATYP | BND.ADDR | BND.PORT |
609           * +------+----------+----------+
610           * |  1   |  > 0     |    2     |
611           * +------+----------+----------+
612           */
613 
614          /* ATYP */
615          if ((rc = socks_recvfromn(s,
616                                    &host->atype, sizeof(host->atype),
617                                    sizeof(host->atype),
618                                    0,
619                                    NULL,
620                                    NULL,
621                                    NULL,
622                                    auth)) != (ssize_t)sizeof(host->atype)) {
623             fmtresponseerror(rc, sizeof(host->atype), emsg, emsglen);
624             return -1;
625          }
626 
627          switch(host->atype) {
628             case SOCKS_ADDR_IPV4:
629                if ((rc = socks_recvfromn(s,
630                                          &host->addr.ipv4,
631                                          sizeof(host->addr.ipv4),
632                                          sizeof(host->addr.ipv4),
633                                          0,
634                                          NULL,
635                                          NULL,
636                                          NULL,
637                                          auth))
638                != (ssize_t)sizeof(host->addr.ipv4)) {
639                   fmtresponseerror(rc, sizeof(host->addr.ipv4), emsg, emsglen);
640                   return -1;
641                }
642                break;
643 
644             case SOCKS_ADDR_IPV6:
645                if ((rc = socks_recvfromn(s,
646                                          &host->addr.ipv6.ip,
647                                          sizeof(host->addr.ipv6.ip),
648                                          sizeof(host->addr.ipv6.ip),
649                                          0,
650                                          NULL,
651                                          NULL,
652                                          NULL,
653                                          auth))
654                != (ssize_t)sizeof(host->addr.ipv6.ip)) {
655                   fmtresponseerror(rc,
656                                    sizeof(host->addr.ipv6.ip),
657                                    emsg,
658                                    emsglen);
659 
660                   return -1;
661                }
662                break;
663 
664             case SOCKS_ADDR_DOMAIN: {
665                unsigned char alen;
666 
667                /* read length of domain name. */
668                if ((rc = socks_recvfromn(s,
669                                          &alen,
670                                          sizeof(alen),
671                                          sizeof(alen),
672                                          0,
673                                          NULL,
674                                          NULL,
675                                          NULL,
676                                          auth)) != (ssize_t)sizeof(alen)) {
677                   fmtresponseerror(rc, sizeof(alen), emsg, emsglen);
678                   return -1;
679                }
680 
681                OCTETIFY(alen);
682 
683 #if MAXHOSTNAMELEN < 0xff
684                SASSERTX(alen < sizeof(host->addr.domain));
685 #endif /* MAXHOSTNAMELEN < 0xff */
686 
687                /* BND.ADDR, alen octets */
688                if ((rc = socks_recvfromn(s,
689                                          host->addr.domain,
690                                          (size_t)alen,
691                                          (size_t)alen,
692                                          0,
693                                          NULL,
694                                          NULL,
695                                          NULL,
696                                          auth)) != (ssize_t)alen) {
697                   fmtresponseerror(rc, alen, emsg, emsglen);
698                   return -1;
699                }
700                host->addr.domain[alen] = NUL;
701 
702                break;
703             }
704 
705             default:
706                swarnx("%s: unsupported address format %d in reply",
707                       function, host->atype);
708                return -1;
709          }
710 
711          /* BND.PORT */
712          if ((rc = socks_recvfromn(s,
713                                    &host->port,
714                                    sizeof(host->port),
715                                    sizeof(host->port),
716                                    0,
717                                    NULL,
718                                    NULL,
719                                    NULL,
720                                    auth)) != (ssize_t)sizeof(host->port)) {
721             fmtresponseerror(rc, sizeof(host->port), emsg, emsglen);
722             return -1;
723          }
724 
725          break;
726    }
727 
728    return 0;
729 }
730 
731 int
serverreplyisok(version,command,reply,route,emsg,emsglen)732 serverreplyisok(version, command, reply, route, emsg, emsglen)
733    const unsigned int version;
734    const unsigned int command;
735    const unsigned int reply;
736    route_t *route;
737    char *emsg;
738    const size_t emsglen;
739 {
740    const char *function = "serverreplyisok()";
741 
742    slog(LOG_NEGOTIATE, "%s: version %d, command %d, reply %d",
743         function, version, command, reply);
744 
745    switch (version) {
746       case PROXY_SOCKS_V4REPLY_VERSION:
747          switch (reply) {
748             case SOCKSV4_SUCCESS:
749                socks_clearblacklist(route);
750                return 1;
751 
752             case SOCKSV4_FAIL:
753                snprintf(emsg, emsglen, "generic proxy server failure");
754 
755                socks_clearblacklist(route);
756                errno = ECONNREFUSED;
757                break;
758 
759             case SOCKSV4_NO_IDENTD:
760                snprintf(emsg, emsglen,
761                         "proxy server says it could not get a ident (rfc931) "
762                         "response from host we are running on");
763 
764                /* will probably fail next time too, so blacklist it. */
765                socks_blacklist(route, emsg);
766 
767                errno = ECONNREFUSED;
768                break;
769 
770             case SOCKSV4_BAD_ID:
771                snprintf(emsg, emsglen,
772                         "proxy server claims username/ident mismatch from us");
773 
774                socks_blacklist(route, emsg);
775                errno = ECONNREFUSED;
776                break;
777 
778             default:
779                snprintf(emsg, emsglen,
780                         "unknown v%d reply from proxy server.  Replycode: %d",
781                         version, reply);
782 
783                socks_blacklist(route, emsg);
784                errno = ECONNREFUSED;
785                break;
786          }
787          break;
788 
789       case PROXY_SOCKS_V5:
790          switch (reply) {
791             case SOCKS_SUCCESS:
792                socks_clearblacklist(route);
793                return 1;
794 
795             case SOCKS_FAILURE:
796                snprintf(emsg, emsglen,
797                         "generic failure at remote proxy server");
798 
799                if (command == SOCKS_BIND) {
800                   errno = EADDRINUSE;
801                   socks_clearblacklist(route);
802                }
803                else
804                   socks_blacklist(route, emsg);
805 
806                errno = ECONNREFUSED;
807                break;
808 
809             case SOCKS_NOTALLOWED:
810                snprintf(emsg, emsglen, "connection denied by proxy server");
811 
812                socks_clearblacklist(route);
813                errno = ECONNREFUSED;
814                break;
815 
816             case SOCKS_NETUNREACH:
817                snprintf(emsg, emsglen, "net unreachable by proxy server");
818 
819                socks_clearblacklist(route);
820                errno = ENETUNREACH;
821                break;
822 
823             case SOCKS_HOSTUNREACH:
824                snprintf(emsg, emsglen, "target unreachable by proxy server");
825 
826                socks_clearblacklist(route);
827                errno = EHOSTUNREACH;
828                break;
829 
830             case SOCKS_CONNREFUSED:
831                snprintf(emsg, emsglen,
832                         "target refused connection by proxy server");
833 
834                socks_clearblacklist(route);
835                errno = ECONNREFUSED;
836                break;
837 
838             case SOCKS_TTLEXPIRED:
839                snprintf(emsg, emsglen,
840                         "connection to target from proxy server timed out");
841 
842                socks_clearblacklist(route);
843                errno = ETIMEDOUT;
844                break;
845 
846             case SOCKS_CMD_UNSUPP:
847                snprintf(emsg, emsglen, "command not supported by proxy server");
848 
849                swarnx("%s: %s", function, emsg);
850 
851                socks_blacklist(route, emsg);
852                errno = ECONNREFUSED;
853                break;
854 
855             case SOCKS_ADDR_UNSUPP:
856                snprintf(emsg, emsglen,
857                         "address format in the request we sent is not "
858                         "supported by the proxy server");
859 
860                socks_blacklist(route, emsg);
861                errno = ECONNREFUSED;
862                break;
863 
864             default:
865                snprintf(emsg, emsglen,
866                         "unknown v%d reply from proxy server: %d",
867                         version, reply);
868 
869                socks_blacklist(route, emsg);
870                errno = ECONNREFUSED;
871                break;
872          }
873          break;
874 
875       case PROXY_HTTP_10:
876       case PROXY_HTTP_11:
877          switch (reply) {
878             case HTTP_SUCCESS:
879                socks_clearblacklist(route);
880                return 1;
881 
882             default:
883                snprintf(emsg, emsglen, "unknown proxy server failure");
884 
885                socks_blacklist(route, emsg);
886                errno = ECONNREFUSED;
887                break;
888          }
889          break;
890 
891       case PROXY_UPNP:
892          switch (reply) {
893             case UPNP_SUCCESS:
894                socks_clearblacklist(route);
895                return 1;
896 
897             default:
898                socks_blacklist(route, "UPNP failure");
899                errno = ECONNREFUSED;
900                break;
901          }
902          break;
903 
904 
905       default:
906          snprintf(emsg, emsglen, "unknown proxy version %d", version);
907          break;
908    }
909 
910    SASSERTX(*emsg != NUL);
911 
912    slog(LOG_DEBUG, "%s", emsg);
913 
914    return 0;
915 }
916 
917 /* ARGSUSED */
918 int
clientmethod_uname(s,host,version,name,password,emsg,emsglen)919 clientmethod_uname(s, host, version, name, password, emsg, emsglen)
920    int s;
921    const sockshost_t *host;
922    int version;
923    unsigned char *name;
924    unsigned char *password;
925    char *emsg;
926    const size_t emsglen;
927 {
928    const char *function = "clientmethod_uname()";
929    static authmethod_uname_t uname;   /* cached userinfo.              */
930    static sockshost_t unamehost;      /* host cache was gotten for.    */
931    static int usecachedinfo;          /* cached data is ok?            */
932    ssize_t rc;
933    size_t len;
934    unsigned char *offset;
935    unsigned char request[ 1               /* version.          */
936                         + 1               /* username length.  */
937                         + MAXNAMELEN      /* username.         */
938                         + 1               /* password length.  */
939                         + MAXPWLEN        /* password.         */
940    ];
941    unsigned char response[ 1 /* version.  */
942                          + 1 /* status.   */
943    ];
944 
945    switch (version) {
946       case PROXY_SOCKS_V5:
947          break;
948 
949       default:
950          SERRX(version);
951    }
952 
953    if (memcmp(&unamehost, host, sizeof(unamehost)) != 0)
954       usecachedinfo = 0;   /* not same host as cache was gotten for. */
955 
956    /* fill in request. */
957 
958    offset  = request;
959    *offset = (unsigned char)SOCKS_UNAMEVERSION;
960    ++offset;
961 
962    if (!usecachedinfo) {
963       if (name == NULL
964       && (name = (unsigned char *)socks_getusername(host,
965                                                     (char *)offset + 1,
966                                                     MAXNAMELEN)) == NULL) {
967          snprintf(emsg, emsglen, "could not determine username of client");
968          return -1;
969       }
970 
971       if (strlen((char *)name) > sizeof(uname.name) - 1) {
972          char visbuf[MAXNAMELEN];
973 
974          swarnx("%s: username \"%s ...\" is too long.  Max length is %lu.  "
975                 "Trying to continue anyway.",
976                 function,
977                 str2vis((char *)name,
978                         strlen((char *)name),
979                         visbuf,
980                         sizeof(visbuf)),
981                 (unsigned long)(sizeof(uname.name) - 1));
982 
983          /* perhaps it will be truncated at proxy too. */
984          name[sizeof(uname.name) - 1] = NUL;
985       }
986 
987       SASSERTX(strlen((char *)name) < sizeof(uname.name));
988       strcpy((char *)uname.name, (char *)name);
989    }
990 
991    slog(LOG_DEBUG, "%s: usecachedinfo %d, name \"%s\"",
992         function, usecachedinfo, uname.name);
993 
994    /* first byte gives length. */
995    *offset = (unsigned char)strlen((char *)uname.name);
996    OCTETIFY(*offset);
997    strcpy((char *)offset + 1, (char *)uname.name);
998    offset += *offset + 1;
999 
1000    if (!usecachedinfo) {
1001       if (password == NULL
1002       && (password = (unsigned char *)socks_getpassword(host,
1003                                                        (char *)name,
1004                                                        (char *)offset + 1,
1005                                                        MAXPWLEN)) == NULL) {
1006          slog(LOG_NEGOTIATE,
1007               "%s: could not determine password of client, using an empty one",
1008               function);
1009 
1010          password = (unsigned char *)"";
1011       }
1012 
1013       if (strlen((char *)password) > sizeof(uname.password) - 1) {
1014          swarnx("%s: password is too long.  Max length is %lu.  "
1015                 "Trying to continue anyway.",
1016                 function, (unsigned long)(sizeof(uname.password) - 1));
1017 
1018          /* perhaps it will be truncated at proxy too. */
1019          password[sizeof(uname.password) - 1] = NUL;
1020       }
1021 
1022       SASSERTX(strlen((char *)password) < sizeof(uname.password));
1023       strcpy((char *)uname.password, (char *)password);
1024    }
1025 
1026    /* first byte gives length. */
1027    *offset = (unsigned char)strlen((char *)uname.password);
1028    OCTETIFY(*offset);
1029    strcpy((char *)offset + 1, (char *)uname.password);
1030    offset += *offset + 1;
1031 
1032    slog(LOG_NEGOTIATE, "%s: offering username \"%s\", password %s to server",
1033         function, uname.name, (*uname.password == NUL) ? "\"\"" : "********");
1034 
1035    len = offset - request;
1036    if ((rc = socks_sendton(s,
1037                            request,
1038                            len,
1039                            len,
1040                            0,
1041                            NULL,
1042                            0,
1043                            NULL,
1044                            NULL)) != (ssize_t)len) {
1045       snprintf(emsg, emsglen,
1046                "send of username/password to proxy server failed, "
1047                "sent %ld/%lu: %s",
1048                (long)rc, (unsigned long)(offset - request), strerror(errno));
1049       return -1;
1050    }
1051 
1052    if ((rc = socks_recvfromn(s,
1053                              response,
1054                              sizeof(response),
1055                              sizeof(response),
1056                              0,
1057                              NULL,
1058                              NULL,
1059                              NULL,
1060                              NULL)) != sizeof(response)) {
1061       snprintfn(emsg, emsglen,
1062                 "failed to receive proxy server response, received %ld/%lu: %s",
1063                 (long)rc, (unsigned long)sizeof(response), strerror(errno));
1064       return -1;
1065    }
1066 
1067    slog(LOG_NEGOTIATE, "%s: received server response: 0x%x, 0x%x",
1068         function, response[0], response[1]);
1069 
1070    if (request[UNAME_VERSION] != response[UNAME_VERSION]) {
1071       snprintf(emsg, emsglen,
1072                "sent a v%d uname request to proxy server, "
1073                "but got back a v%d response",
1074                request[0], response[1]);
1075       return -1;
1076    }
1077 
1078    if (response[UNAME_STATUS] == UNAME_STATUS_ISOK) { /* server accepted. */
1079       unamehost     = *host;
1080       usecachedinfo = 1;
1081 
1082       return 0;
1083    }
1084 
1085    snprintf(emsg, emsglen, "proxy server rejected our username/password");
1086 
1087    return -1;
1088 }
1089 
1090 #if HAVE_GSSAPI
1091  /*
1092   * This code was contributed by
1093   * Markus Moeller (markus_moeller at compuserve.com).
1094   */
1095 
1096 int
clientmethod_gssapi(s,protocol,gw,version,auth,emsg,emsglen)1097 clientmethod_gssapi(s, protocol, gw, version, auth, emsg, emsglen)
1098    int s;
1099    int protocol;
1100    const gateway_t *gw;
1101    int version;
1102    authmethod_t *auth;
1103    char *emsg;
1104    const size_t emsglen;
1105 {
1106    const char *function = "clientmethod_gssapi()";
1107    OM_uint32 ret_flags, major_status, minor_status;
1108    gss_name_t            client_name       = GSS_C_NO_NAME,
1109                          server_name       = GSS_C_NO_NAME;
1110    gss_cred_id_t         server_creds      = GSS_C_NO_CREDENTIAL;
1111    gss_buffer_desc       service           = GSS_C_EMPTY_BUFFER,
1112                          input_token       = GSS_C_EMPTY_BUFFER,
1113                          output_token      = GSS_C_EMPTY_BUFFER,
1114                          *context_token    = GSS_C_NO_BUFFER;
1115 #if SOCKS_CLIENT
1116    sigset_t oldset;
1117 #endif /* SOCKS_CLIENT */
1118    ssize_t rc;
1119    size_t len;
1120    unsigned short token_length;
1121    unsigned char request[GSSAPI_HLEN + MAXGSSAPITOKENLEN],
1122                  response[GSSAPI_HLEN + MAXGSSAPITOKENLEN],
1123                  gss_server_enc, gss_enc;
1124    char nameinfo[MAXNAMELEN + MAXNAMELEN], buf[sizeof(nameinfo)], tmpbuf[512];
1125    int conf_state;
1126    int have_slash;
1127 
1128 #if SOCKSLIBRARY_DYNAMIC && SOCKS_CLIENT
1129    /*
1130     * Make sure the gssapi functions use the native connect(2)/bind(2)
1131     * and sendto(2)/recvfrom(2) system calls, even if the user has not
1132     * created a direct route for the related addresses.
1133     *
1134     * During the process of establishing a socks gssapi session with
1135     * server X, we will need to contact the kdc for a ticket to
1136     * use with server X, but if our only route to the kdc is via
1137     * server X, that doesn't work of course.  The correct thing
1138     * is for the user to create a direct route to kdc, but helping
1139     * him this way will hopefully save a ton of grief.
1140     *
1141     * It will not work in the (hopefully very rare) case where the
1142     * user actually has set things up correctly, but the route to
1143     * the kdc is via another, non-gssapi, proxy though.
1144     *
1145     * Based on an idea by Markus Moeller for forcing connect(2) to
1146     * the Kerberos kdc to use the native connect(2), rather than most
1147     * likely create a routing loop when the user neglects to mark the
1148     * route to the kdc as "direct" in socks.conf.
1149     */
1150    socks_mark_io_as_native();
1151 #endif /* SOCKSLIBRARY_DYNAMIC && SOCKS_CLIENT */
1152 
1153 
1154    /*
1155     * Get the hostname of the gateway so we can convert it to a gss-name
1156     * and contact the kdc to get a ticket for using the socks-service at
1157     * hostname.
1158     */
1159 
1160    SASSERTX(gw != NULL);
1161    switch (gw->addr.atype) {
1162       case SOCKS_ADDR_IPV4: {
1163          struct sockaddr_storage addr;
1164 
1165          sockshost2sockaddr(&gw->addr, &addr);
1166 
1167          SOCKS_SIGBLOCK_IF_CLIENT(SIGIO, &oldset);
1168          rc = getnameinfo(TOSA(&addr),
1169                           salen(addr.ss_family),
1170                           nameinfo,
1171                           sizeof(nameinfo),
1172                           NULL,
1173                           0,
1174                           NI_NAMEREQD);
1175          SOCKS_SIGUNBLOCK_IF_CLIENT(&oldset);
1176 
1177          if (rc != 0) {
1178             char ntop[MAXSOCKADDRSTRING];
1179 
1180             if (inet_ntop(addr.ss_family,
1181                           GET_SOCKADDRADDR(&addr),
1182                           ntop,
1183                           sizeof(ntop)) == NULL) {
1184                snprintf(emsg, emsglen, "inet_ntop(3) failed on addr %s: %s",
1185                          sockaddr2string2(&addr, 0, NULL, 0), strerror(errno));
1186             }
1187             else
1188                snprintf(emsg, emsglen, "getnameinfo(%s) failed: error %ld",
1189                         ntop, (long)rc);
1190 
1191             swarnx("%s: %s", emsg, function); /* likely generic config error. */
1192             goto error;
1193          }
1194          break;
1195       }
1196 
1197       case SOCKS_ADDR_DOMAIN:
1198          STRCPY_ASSERTSIZE(nameinfo, gw->addr.addr.domain);
1199          break;
1200 
1201       default:
1202          SERRX(gw->addr.atype);
1203    }
1204 
1205    have_slash = (strchr(gw->state.gssapiservicename, '/') != NULL);
1206    if (have_slash)
1207       snprintf(buf, sizeof(buf), "%s", gw->state.gssapiservicename);
1208    else
1209       snprintf(buf, sizeof(buf), "%s@%s", gw->state.gssapiservicename, nameinfo);
1210    service.value  = buf;
1211    service.length = strlen((char *)service.value);
1212 
1213    SOCKS_SIGBLOCK_IF_CLIENT(SIGIO, &oldset);
1214    major_status = gss_import_name(&minor_status,
1215                                   &service,
1216                                   have_slash ? (gss_OID)GSS_C_NULL_OID
1217                                              : (gss_OID)gss_nt_service_name,
1218                                   &server_name);
1219    SOCKS_SIGUNBLOCK_IF_CLIENT(&oldset);
1220 
1221    if (gss_err_isset(major_status, minor_status, tmpbuf, sizeof(tmpbuf))) {
1222       snprintf(emsg, emsglen, "gss_import_name() failed: %s", emsg);
1223 
1224       SOCKS_SIGUNBLOCK_IF_CLIENT(&oldset);
1225       goto error;
1226    }
1227 
1228    request[GSSAPI_VERSION]     = SOCKS_GSSAPI_VERSION;
1229    auth->mdata.gssapi.state.id = GSS_C_NO_CONTEXT;
1230 
1231    while (1) {
1232       SOCKS_SIGBLOCK_IF_CLIENT(SIGIO, &oldset);
1233       major_status = gss_init_sec_context(&minor_status,
1234                                           GSS_C_NO_CREDENTIAL,
1235                                           &auth->mdata.gssapi.state.id,
1236                                           server_name,
1237                                           GSS_C_NULL_OID,
1238                                             GSS_C_MUTUAL_FLAG
1239                                           | GSS_C_REPLAY_FLAG
1240                                           | (protocol == SOCKS_TCP ?
1241                                                       GSS_C_SEQUENCE_FLAG : 0),
1242                                           /*
1243                                            * | GSS_C_DELEG_FLAG
1244                                            * RFC 1961 says GSS_C_DELEG_FLAG
1245                                            * should also be set, but I can't
1246                                            * see any reason why the client
1247                                            * should want to forward it's
1248                                            * tickets to a socks server ...
1249                                            *
1250                                            * Don't set unless until we find
1251                                            * a reason to do so.
1252                                            */
1253                                           0,
1254                                           GSS_C_NO_CHANNEL_BINDINGS,
1255                                           context_token,
1256                                           NULL,
1257                                           &output_token,
1258                                           &ret_flags,
1259                                           NULL);
1260       SOCKS_SIGUNBLOCK_IF_CLIENT(&oldset);
1261 
1262       slog(LOG_DEBUG, "%s: length of output_token is %lu",
1263            function, (unsigned long)output_token.length);
1264 
1265       switch (major_status) {
1266          case GSS_S_COMPLETE:
1267             slog(LOG_NEGOTIATE, "%s: gssapi negotiation completed", function);
1268             break;
1269 
1270          case GSS_S_CONTINUE_NEEDED:
1271             slog(LOG_DEBUG, "%s: gssapi negotiation to be continued", function);
1272             break;
1273 
1274          default:
1275             if (!gss_err_isset(major_status,
1276                                minor_status,
1277                                tmpbuf,
1278                                sizeof(tmpbuf)))
1279                snprintf(tmpbuf, sizeof(tmpbuf), "unknown gss major_status %d",
1280                         major_status);
1281 
1282             snprintf(emsg, emsglen,
1283                      "gss_init_sec_context() failed: %s", tmpbuf);
1284             goto error;
1285       }
1286 
1287       if(output_token.length != 0) {
1288          request[GSSAPI_STATUS] = SOCKS_GSSAPI_AUTHENTICATION;
1289 
1290          token_length = htons((unsigned short)output_token.length);
1291          memcpy(request + GSSAPI_TOKEN_LENGTH, &token_length, sizeof(short));
1292 
1293          SASSERTX(output_token.length <= sizeof(request) - GSSAPI_HLEN);
1294          memcpy(request + GSSAPI_HLEN, output_token.value, output_token.length);
1295 
1296          slog(LOG_DEBUG, "%s: sending token of length %lu to server",
1297               function, (unsigned long)output_token.length);
1298 
1299          len = (size_t)(GSSAPI_HLEN + output_token.length);
1300          if ((rc = socks_sendton(s,
1301                                  request,
1302                                  len,
1303                                  len,
1304                                  0,
1305                                  NULL,
1306                                  0,
1307                                  NULL,
1308                                  NULL)) != (ssize_t)len) {
1309             snprintf(emsg, emsglen,
1310                      "send of request to proxy server failed, sent %ld/%ld: %s",
1311                      (long)rc,
1312                      (long)(GSSAPI_HLEN + output_token.length),
1313                      strerror(errno));
1314             goto error;
1315          }
1316 
1317          CLEAN_GSS_TOKEN(output_token);
1318       }
1319 
1320       if (major_status == GSS_S_COMPLETE)
1321          break;
1322 
1323       if ((rc = socks_recvfromn(s,
1324                                 response,
1325                                 GSSAPI_HLEN,
1326                                 GSSAPI_HLEN,
1327                                 0,
1328                                 NULL,
1329                                 NULL,
1330                                 NULL,
1331                                 NULL)) != GSSAPI_HLEN) {
1332          snprintf(emsg, emsglen,
1333                   "read of response from proxy server failed, read %ld/%ld: %s",
1334                   (long)rc, (long)GSSAPI_HLEN, strerror(errno));
1335          goto error;
1336       }
1337 
1338       slog(LOG_DEBUG, "%s: read %ld bytes of response data from server",
1339            function, (long)rc);
1340 
1341       if(response[GSSAPI_VERSION] != SOCKS_GSSAPI_VERSION) {
1342          snprintf(emsg, emsglen,
1343                   "invalid GSSAPI authentication response type (%d, %d) from "
1344                   "proxy server",
1345                   response[GSSAPI_VERSION], response[GSSAPI_STATUS]);
1346          goto error;
1347       }
1348 
1349       if (response[GSSAPI_STATUS] == 0xff) {
1350          snprintf(emsg, emsglen, "user was rejected by proxy server (%d, %d).",
1351                   response[GSSAPI_VERSION], response[GSSAPI_STATUS]);
1352          goto error;
1353       }
1354 
1355       if(response[GSSAPI_STATUS] != SOCKS_GSSAPI_AUTHENTICATION) {
1356          snprintf(emsg, emsglen,
1357                   "invalid GSSAPI authentication response type (%d, %d) from "
1358                   "proxy server",
1359                   response[GSSAPI_VERSION], response[GSSAPI_STATUS]);
1360          goto error;
1361       }
1362 
1363       memcpy(&token_length, &response[GSSAPI_TOKEN_LENGTH], sizeof(short));
1364       token_length = ntohs(token_length);
1365 
1366       input_token.value = response + GSSAPI_HLEN;
1367       if ((input_token.length = token_length) > sizeof(response) - GSSAPI_HLEN){
1368          snprintf(emsg, emsglen,
1369                   "proxy server sent illegal token length of %u, max is %lu",
1370                   token_length, (long unsigned)sizeof(response) - GSSAPI_HLEN);
1371          goto error;
1372       }
1373 
1374       if ((rc = socks_recvfromn(s,
1375                                 input_token.value,
1376                                 input_token.length,
1377                                 input_token.length,
1378                                 0,
1379                                 NULL,
1380                                 NULL,
1381                                 NULL,
1382                                 NULL)) != (ssize_t)input_token.length) {
1383          snprintf(emsg, emsglen,
1384                   "read of response from proxy server failed, read %ld/%ld: %s",
1385                   (long)rc, (long)input_token.length, strerror(errno));
1386 
1387          goto error;
1388       }
1389 
1390       slog(LOG_DEBUG, "%s: read %lu byte token from server",
1391            function, (long)input_token.length);
1392 
1393       context_token = &input_token;
1394    }
1395 
1396    CLEAN_GSS_TOKEN(output_token);
1397    CLEAN_GSS_AUTH(client_name, server_name, server_creds);
1398 
1399    request[GSSAPI_STATUS] = SOCKS_GSSAPI_ENCRYPTION;
1400 
1401    /*
1402     * offer the best we are configured to support.
1403     * Later when we get the reply from the server, check that it is, if
1404     * not the one we offered, at least one we are configured to support
1405     * for this rule.
1406     */
1407    if (gw->state.gssapiencryption.confidentiality)
1408       gss_enc = SOCKS_GSSAPI_CONFIDENTIALITY;
1409    else if (gw->state.gssapiencryption.integrity)
1410       gss_enc = SOCKS_GSSAPI_INTEGRITY;
1411    else if (gw->state.gssapiencryption.clear)
1412       gss_enc = SOCKS_GSSAPI_CLEAR;
1413    else {
1414       swarnx("%s: no gssapi type to offer set", function);
1415       SERRX(0);
1416    }
1417 
1418    slog(LOG_NEGOTIATE,
1419         "%s: running in %s %s GSSAPI mode.  Offering server protection "
1420         "\"%s\" (%d)",
1421         function,
1422         gw->state.gssapiencryption.nec ? "nec"          : "rfc1961",
1423         gw->state.gssapiencryption.nec ? "non-standard" : "standard",
1424         gssapiprotection2string(gss_enc), gss_enc);
1425 
1426    if (gw->state.gssapiencryption.nec) {
1427       const size_t tosend = GSSAPI_HLEN + 1, toread = tosend;
1428 
1429       token_length = htons(1);
1430       memcpy(&request[GSSAPI_TOKEN_LENGTH], &token_length, sizeof(short));
1431       memcpy(request + GSSAPI_HLEN, &gss_enc, 1);
1432 
1433       if ((rc = socks_sendton(s,
1434                               request,
1435                               tosend,
1436                               tosend,
1437                               0,
1438                               NULL,
1439                               0,
1440                               NULL,
1441                               NULL)) != (ssize_t)tosend) {
1442          snprintf(emsg, emsglen,
1443                   "send of request to proxy server failed, sent %ld/%ld: %s",
1444                   (long)rc, (long)tosend, strerror(errno));
1445          goto error;
1446       }
1447 
1448       if ((rc = socks_recvfromn(s,
1449                                 response,
1450                                 toread,
1451                                 toread,
1452                                 0,
1453                                 NULL,
1454                                 NULL,
1455                                 NULL,
1456                                 NULL)) != (ssize_t)toread) {
1457          snprintf(emsg, emsglen,
1458                   "read of response from proxy server failed, read %ld/%ld: %s",
1459                   (long)rc, (long)(GSSAPI_HLEN + 1), strerror(errno));
1460          goto error;
1461       }
1462 
1463       if (response[GSSAPI_STATUS] != SOCKS_GSSAPI_ENCRYPTION) {
1464          snprintf(emsg, emsglen,
1465                   "invalid GSSAPI encryption response type (%d, %d) "
1466                   "from proxy server",
1467                   response[GSSAPI_VERSION], response[GSSAPI_STATUS]);
1468          goto error;
1469       }
1470 
1471       memcpy(&token_length, &response[GSSAPI_TOKEN_LENGTH], sizeof(short));
1472       token_length = ntohs(token_length);
1473 
1474       if (token_length != 1) {
1475          snprintf(emsg, emsglen,
1476                   "received invalid encryption token length from proxy server "
1477                   "(%d, not 1) ",
1478                   token_length);
1479          goto error;
1480       }
1481 
1482       gss_server_enc = response[GSSAPI_TOKEN];
1483    }
1484    else {
1485       unsigned char p;
1486 
1487       input_token.value  = &p;
1488       input_token.length = 1;
1489 
1490       memcpy(input_token.value, &gss_enc, input_token.length);
1491 
1492       SOCKS_SIGBLOCK_IF_CLIENT(SIGIO, &oldset);
1493       major_status = gss_wrap(&minor_status,
1494                               auth->mdata.gssapi.state.id,
1495                               0,
1496                               GSS_C_QOP_DEFAULT,
1497                               &input_token,
1498                               &conf_state,
1499                               &output_token);
1500       SOCKS_SIGUNBLOCK_IF_CLIENT(&oldset);
1501 
1502       if (gss_err_isset(major_status, minor_status, tmpbuf, sizeof(tmpbuf))) {
1503          snprintf(emsg, emsglen, "gss_wrap() failed: %s", tmpbuf);
1504          goto error;
1505       }
1506 
1507       token_length = htons((short)output_token.length);
1508       memcpy(&request[GSSAPI_TOKEN_LENGTH], &token_length, sizeof(short));
1509 
1510       if ((rc = socks_sendton(s,
1511                               request,
1512                               GSSAPI_TOKEN,
1513                               GSSAPI_TOKEN,
1514                               0,
1515                               NULL,
1516                               0,
1517                               NULL,
1518                               NULL)) != GSSAPI_TOKEN)  {
1519          snprintf(emsg, emsglen,
1520                   "send of request to proxy server failed, sent %ld/%ld: %s",
1521                   (long)rc, (long)GSSAPI_TOKEN, strerror(errno));
1522          goto error;
1523       }
1524 
1525       if ((rc = socks_sendton(s,
1526                               output_token.value,
1527                               output_token.length,
1528                               output_token.length,
1529                               0,
1530                               NULL,
1531                               0,
1532                               NULL,
1533                               NULL)) != (ssize_t)output_token.length) {
1534          snprintf(emsg, emsglen,
1535                   "send of request to proxy server failed, sent %ld/%ld: %s",
1536                   (long)rc, (long)output_token.length, strerror(errno));
1537          goto error;
1538       }
1539 
1540       CLEAN_GSS_TOKEN(output_token);
1541 
1542       if ((rc = socks_recvfromn(s,
1543                                 response,
1544                                 GSSAPI_HLEN,
1545                                 GSSAPI_HLEN,
1546                                 0,
1547                                 NULL,
1548                                 NULL,
1549                                 NULL,
1550                                 NULL)) != GSSAPI_HLEN) {
1551          snprintf(emsg, emsglen,
1552                   "read of response from proxy server failed, read %ld/%d: %s",
1553                   (long)rc, GSSAPI_HLEN, strerror(errno));
1554          goto error;
1555       }
1556 
1557       if (response[GSSAPI_STATUS] != SOCKS_GSSAPI_ENCRYPTION) {
1558          snprintf(emsg, emsglen,
1559                   "received invalid GSSAPI encryption response type from "
1560                   "proxy server (version %d, status %d)",
1561                   response[GSSAPI_VERSION], response[GSSAPI_STATUS]);
1562          goto error;
1563       }
1564 
1565       memcpy(&token_length, response + GSSAPI_TOKEN_LENGTH, sizeof(short));
1566       input_token.length = ntohs(token_length);
1567 
1568       if (input_token.length > sizeof(response) - GSSAPI_HLEN) {
1569          snprintf(emsg, emsglen,
1570                   "proxy server replied with too big a token of length %u, "
1571                   "but the max length is %lu",
1572                   (unsigned)token_length,
1573                   (unsigned long)sizeof(response) - GSSAPI_HLEN);
1574          goto error;
1575       }
1576 
1577       input_token.value = response + GSSAPI_HLEN;
1578 
1579       if ((rc = socks_recvfromn(s,
1580                                 input_token.value,
1581                                 input_token.length,
1582                                 input_token.length,
1583                                 0,
1584                                 NULL,
1585                                 NULL,
1586                                 NULL,
1587                                 NULL)) != (ssize_t)input_token.length) {
1588          snprintf(emsg, emsglen,
1589                   "read of response from proxy server failed, read %ld/%ld: %s",
1590                   (long)rc, (long)input_token.length, strerror(errno));
1591          goto error;
1592       }
1593 
1594       major_status = gss_unwrap(&minor_status,
1595                                 auth->mdata.gssapi.state.id,
1596                                 &input_token,
1597                                 &output_token,
1598                                 0,
1599                                 GSS_C_QOP_DEFAULT);
1600 
1601       if (gss_err_isset(major_status, minor_status, tmpbuf, sizeof(tmpbuf))) {
1602          snprintf(emsg, emsglen,
1603                   "gss_unwrap() of token received from proxy server failed: %s",
1604                   tmpbuf);
1605          goto error;
1606       }
1607 
1608       if (output_token.length != 1)  {
1609          snprintf(emsg, emsglen,
1610                   "gssapi encryption output_token.length is not 1 as expected, "
1611                   "but instead %lu",
1612                   (unsigned long)output_token.length);
1613          goto error;
1614       }
1615 
1616       gss_server_enc = *(unsigned char *)output_token.value;
1617 
1618       CLEAN_GSS_TOKEN(output_token);
1619    }
1620 
1621    switch (gss_server_enc) {
1622       case SOCKS_GSSAPI_CLEAR:
1623          if (gw->state.gssapiencryption.clear)
1624             gss_enc = SOCKS_GSSAPI_CLEAR;
1625          break;
1626 
1627       case SOCKS_GSSAPI_INTEGRITY:
1628          if (gw->state.gssapiencryption.integrity)
1629             gss_enc = SOCKS_GSSAPI_INTEGRITY;
1630          break;
1631 
1632       case SOCKS_GSSAPI_CONFIDENTIALITY:
1633          if (gw->state.gssapiencryption.confidentiality)
1634             gss_enc = SOCKS_GSSAPI_CONFIDENTIALITY;
1635          break;
1636 
1637       default:
1638          snprintf(emsg, emsglen,
1639                   "proxy server responded with different encryption than we "
1640                   "are accepting for this proxy server.  "
1641                   "Our settings for this server are: "
1642                   "clear/%s, integrity/%s, confidentiality/%s, per message/%s, "
1643                   "but server instead offered us %s (%d)",
1644                   gw->state.gssapiencryption.clear           ? "yes" : "no",
1645                   gw->state.gssapiencryption.integrity       ? "yes" : "no",
1646                   gw->state.gssapiencryption.confidentiality ? "yes" : "no",
1647                   gw->state.gssapiencryption.permessage      ? "yes" : "no",
1648                   gssapiprotection2string(gss_server_enc), gss_server_enc);
1649          goto error;
1650    }
1651 
1652    slog(LOG_NEGOTIATE, "%s: using gssapi %s protection (%d)",
1653         function, gssapiprotection2string(gss_enc), gss_enc);
1654 
1655    auth->mdata.gssapi.state.protection = gss_enc;
1656    if (auth->mdata.gssapi.state.protection != SOCKS_GSSAPI_CLEAR)
1657        auth->mdata.gssapi.state.wrap = 1;
1658 
1659    major_status
1660    = gss_wrap_size_limit(&minor_status,
1661                          auth->mdata.gssapi.state.id,
1662                          auth->mdata.gssapi.state.protection
1663                          == GSSAPI_CONFIDENTIALITY ?
1664                          GSS_REQ_CONF : GSS_REQ_INT,
1665                          GSS_C_QOP_DEFAULT,
1666                          MAXGSSAPITOKENLEN - GSSAPI_HLEN,
1667                          &auth->mdata.gssapi.state.maxgssdata);
1668 
1669    if (gss_err_isset(major_status, minor_status, tmpbuf, sizeof(tmpbuf))) {
1670       snprintf(emsg, emsglen, "gss_wrap_size_limit() failed: %s", tmpbuf);
1671       goto error;
1672    }
1673    else {
1674       slog(LOG_DEBUG, "%s: max length of gssdata before encoding: %lu",
1675            function, (unsigned long)auth->mdata.gssapi.state.maxgssdata);
1676 
1677       if ((unsigned long)auth->mdata.gssapi.state.maxgssdata == 0) {
1678          snprintf(emsg, emsglen,
1679                   "for a token of length %lu gss_wrap_size_limit() returned "
1680                   "%lu, which does not make sense.  "
1681                   "Possibly the kerberos library in use does not fully support "
1682                   "the configured gssapi encoding type?",
1683                    (unsigned long)auth->mdata.gssapi.state.maxgssdata,
1684                    (unsigned long)auth->mdata.gssapi.state.maxgssdata);
1685          goto error;
1686       }
1687    }
1688 
1689 #if SOCKSLIBRARY_DYNAMIC && SOCKS_CLIENT
1690    socks_mark_io_as_normal();
1691 #endif /* SOCKSLIBRARY_DYNAMIC && SOCKS_CLIENT */
1692 
1693    return 0;
1694 
1695 error:
1696    if (auth->mdata.gssapi.state.id != GSS_C_NO_CONTEXT) {
1697       if ((major_status
1698       = gss_delete_sec_context(&minor_status,
1699                                &auth->mdata.gssapi.state.id,
1700                                GSS_C_NO_BUFFER)) != GSS_S_COMPLETE) {
1701          if (!gss_err_isset(major_status, minor_status, tmpbuf, sizeof(tmpbuf)))
1702             *tmpbuf = NUL;
1703 
1704          swarnx("%s: gss_delete_sec_context() failed%s%s",
1705                 function,
1706                 *tmpbuf == NUL ? "" : ": ",
1707                 *tmpbuf == NUL ? "" : tmpbuf);
1708       }
1709    }
1710 
1711    CLEAN_GSS_TOKEN(output_token);
1712    CLEAN_GSS_AUTH(client_name, server_name, server_creds);
1713 
1714 #if SOCKSLIBRARY_DYNAMIC && SOCKS_CLIENT
1715    socks_mark_io_as_normal();
1716 #endif /* SOCKSLIBRARY_DYNAMIC && SOCKS_CLIENT */
1717 
1718    slog(LOG_NEGOTIATE, "%s: failed, error is: %s", function, emsg);
1719    return -1;
1720 }
1721 #endif /* HAVE_GSSAPI and Markus' contributed code. */
1722