1 /* Copyright StrongLoop, Inc. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "s5.h"
23 #include <errno.h>
24 #include <stdint.h>
25 #include <stdlib.h>  /* abort() */
26 #include <string.h>  /* memset() */
27 
28 enum {
29   s5_version,
30   s5_nmethods,
31   s5_methods,
32   s5_auth_pw_version,
33   s5_auth_pw_userlen,
34   s5_auth_pw_username,
35   s5_auth_pw_passlen,
36   s5_auth_pw_password,
37   s5_req_version,
38   s5_req_cmd,
39   s5_req_reserved,
40   s5_req_atyp,
41   s5_req_atyp_host,
42   s5_req_daddr,
43   s5_req_dport0,
44   s5_req_dport1,
45   s5_dead
46 };
47 
s5_init(s5_ctx * cx)48 void s5_init(s5_ctx *cx) {
49   memset(cx, 0, sizeof(*cx));
50   cx->state = s5_version;
51 }
52 
s5_parse(s5_ctx * cx,uint8_t ** data,size_t * size)53 s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size) {
54   s5_err err;
55   uint8_t *p;
56   uint8_t c;
57   size_t i;
58   size_t n;
59 
60   p = *data;
61   n = *size;
62   i = 0;
63 
64   while (i < n) {
65     c = p[i];
66     i += 1;
67     switch (cx->state) {
68       case s5_version:
69         if (c != 5) {
70           err = s5_bad_version;
71           goto out;
72         }
73         cx->state = s5_nmethods;
74         break;
75 
76       case s5_nmethods:
77         cx->arg0 = 0;
78         cx->arg1 = c;  /* Number of bytes to read. */
79         cx->state = s5_methods;
80         break;
81 
82       case s5_methods:
83         if (cx->arg0 < cx->arg1) {
84           switch (c) {
85             case 0:
86               cx->methods |= S5_AUTH_NONE;
87               break;
88             case 1:
89               cx->methods |= S5_AUTH_GSSAPI;
90               break;
91             case 2:
92               cx->methods |= S5_AUTH_PASSWD;
93               break;
94             /* Ignore everything we don't understand. */
95           }
96           cx->arg0 += 1;
97         }
98         if (cx->arg0 == cx->arg1) {
99           err = s5_auth_select;
100           goto out;
101         }
102         break;
103 
104       case s5_auth_pw_version:
105         if (c != 1) {
106           err = s5_bad_version;
107           goto out;
108         }
109         cx->state = s5_auth_pw_userlen;
110         break;
111 
112       case s5_auth_pw_userlen:
113         cx->arg0 = 0;
114         cx->userlen = c;
115         cx->state = s5_auth_pw_username;
116         break;
117 
118       case s5_auth_pw_username:
119         if (cx->arg0 < cx->userlen) {
120           cx->username[cx->arg0] = c;
121           cx->arg0 += 1;
122         }
123         if (cx->arg0 == cx->userlen) {
124           cx->username[cx->userlen] = '\0';
125           cx->state = s5_auth_pw_passlen;
126         }
127         break;
128 
129       case s5_auth_pw_passlen:
130         cx->arg0 = 0;
131         cx->passlen = c;
132         cx->state = s5_auth_pw_password;
133         break;
134 
135       case s5_auth_pw_password:
136         if (cx->arg0 < cx->passlen) {
137           cx->password[cx->arg0] = c;
138           cx->arg0 += 1;
139         }
140         if (cx->arg0 == cx->passlen) {
141           cx->password[cx->passlen] = '\0';
142           cx->state = s5_req_version;
143           err = s5_auth_verify;
144           goto out;
145         }
146         break;
147 
148       case s5_req_version:
149         if (c != 5) {
150           err = s5_bad_version;
151           goto out;
152         }
153         cx->state = s5_req_cmd;
154         break;
155 
156       case s5_req_cmd:
157         switch (c) {
158           case 1:  /* TCP connect */
159             cx->cmd = s5_cmd_tcp_connect;
160             break;
161           case 3:  /* UDP associate */
162             cx->cmd = s5_cmd_udp_assoc;
163             break;
164           default:
165             err = s5_bad_cmd;
166             goto out;
167         }
168         cx->state = s5_req_reserved;
169         break;
170 
171       case s5_req_reserved:
172         cx->state = s5_req_atyp;
173         break;
174 
175       case s5_req_atyp:
176         cx->arg0 = 0;
177         switch (c) {
178           case 1:  /* IPv4, four octets. */
179             cx->state = s5_req_daddr;
180             cx->atyp = s5_atyp_ipv4;
181             cx->arg1 = 4;
182             break;
183           case 3:  /* Hostname.  First byte is length. */
184             cx->state = s5_req_atyp_host;
185             cx->atyp = s5_atyp_host;
186             cx->arg1 = 0;
187             break;
188           case 4:  /* IPv6, sixteen octets. */
189             cx->state = s5_req_daddr;
190             cx->atyp = s5_atyp_ipv6;
191             cx->arg1 = 16;
192             break;
193           default:
194             err = s5_bad_atyp;
195             goto out;
196         }
197         break;
198 
199       case s5_req_atyp_host:
200         cx->arg1 = c;
201         cx->state = s5_req_daddr;
202         break;
203 
204       case s5_req_daddr:
205         if (cx->arg0 < cx->arg1) {
206           cx->daddr[cx->arg0] = c;
207           cx->arg0 += 1;
208         }
209         if (cx->arg0 == cx->arg1) {
210           cx->daddr[cx->arg1] = '\0';
211           cx->state = s5_req_dport0;
212         }
213         break;
214 
215       case s5_req_dport0:
216         cx->dport = c << 8;
217         cx->state = s5_req_dport1;
218         break;
219 
220       case s5_req_dport1:
221         cx->dport |= c;
222         cx->state = s5_dead;
223         err = s5_exec_cmd;
224         goto out;
225 
226       case s5_dead:
227         break;
228 
229       default:
230         abort();
231     }
232   }
233   err = s5_ok;
234 
235 out:
236   *data = p + i;
237   *size = n - i;
238   return err;
239 }
240 
s5_auth_methods(const s5_ctx * cx)241 unsigned int s5_auth_methods(const s5_ctx *cx) {
242   return cx->methods;
243 }
244 
s5_select_auth(s5_ctx * cx,s5_auth_method method)245 int s5_select_auth(s5_ctx *cx, s5_auth_method method) {
246   int err;
247 
248   err = 0;
249   switch (method) {
250     case S5_AUTH_NONE:
251       cx->state = s5_req_version;
252       break;
253     case S5_AUTH_PASSWD:
254       cx->state = s5_auth_pw_version;
255       break;
256     default:
257       err = -EINVAL;
258   }
259 
260   return err;
261 }
262 
s5_strerror(s5_err err)263 const char *s5_strerror(s5_err err) {
264 #define S5_ERR_GEN(_, name, errmsg) case s5_ ## name: return errmsg;
265   switch (err) {
266     S5_ERR_MAP(S5_ERR_GEN)
267     default: ;  /* Silence s5_max_errors -Wswitch warning. */
268   }
269 #undef S5_ERR_GEN
270   return "Unknown error.";
271 }
272