1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * Copyright (c) 1985, 1989 Regents of the University of California.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #include "ftp_var.h"
40 #include <sys/types.h>
41 #include <gssapi/gssapi.h>
42 #include <gssapi/gssapi_ext.h>
43
44 int auth_type; /* Authentication succeeded? If so, what type? */
45
46 char *radix_error(int);
47 static void get_inet_addr_info(struct sockaddr_in6 *, gss_buffer_t);
48
49 static char *radixN =
50 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
51 static char radix_pad = '=';
52
53 /*
54 * authenticate the user, if auth_type is AUTHTYPE_NONE
55 *
56 * Returns: 0 if there is no auth type
57 * 1 if success
58 * 2 if failure
59 */
60
61 gss_OID mechoid;
62 gss_ctx_id_t gcontext; /* global gss security context */
63 static const char *gss_trials[] = { "ftp", "host" };
64 /* the number of elements in gss_trials array */
65 static const int n_gss_trials = sizeof (gss_trials)/sizeof (char *);
66 char *reply_parse;
67
68 int
do_auth(void)69 do_auth(void)
70 {
71 int oldverbose = verbose;
72 uchar_t *out_buf = NULL;
73 size_t outlen;
74 int i;
75
76 if (auth_type != AUTHTYPE_NONE)
77 return (1); /* auth already succeeded */
78
79 /* Other auth types go here ... */
80
81 if (command("AUTH %s", "GSSAPI") == CONTINUE) {
82 OM_uint32 maj_stat, min_stat;
83 gss_name_t target_name;
84 gss_buffer_desc send_tok, recv_tok, *token_ptr;
85 gss_buffer_desc temp_buf;
86 char stbuf[FTPBUFSIZ];
87 int comcode, trial;
88 int req_flags;
89 struct gss_channel_bindings_struct chan;
90
91 get_inet_addr_info(&myctladdr, &temp_buf);
92 chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32 */
93 chan.initiator_address.length = temp_buf.length;
94 chan.initiator_address.value = malloc(temp_buf.length);
95 memcpy(chan.initiator_address.value, temp_buf.value,
96 temp_buf.length);
97
98 get_inet_addr_info(&remctladdr, &temp_buf);
99 chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
100 chan.acceptor_address.length = temp_buf.length;
101 chan.acceptor_address.value = malloc(temp_buf.length);
102 memcpy(chan.acceptor_address.value, temp_buf.value,
103 temp_buf.length);
104
105 chan.application_data.length = 0;
106 chan.application_data.value = 0;
107
108 if (verbose)
109 (void) printf("GSSAPI accepted as authentication type\n");
110
111 /* set the forward flag */
112 req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
113
114 if (fflag)
115 req_flags |= GSS_C_DELEG_FLAG;
116
117 /* blob from gss-client */
118 for (trial = 0; trial < n_gss_trials; trial++) {
119 /* ftp@hostname first, then host@hostname */
120 /* the V5 GSSAPI binding canonicalizes this for us... */
121 (void) snprintf(stbuf, FTPBUFSIZ, "%s@%s",
122 gss_trials[trial], hostname);
123 if (debug)
124 (void) fprintf(stderr,
125 "Trying to authenticate to <%s>\n", stbuf);
126
127 send_tok.value = stbuf;
128 send_tok.length = strlen(stbuf) + 1;
129 maj_stat = gss_import_name(&min_stat, &send_tok,
130 GSS_C_NT_HOSTBASED_SERVICE, &target_name);
131
132 if (maj_stat != GSS_S_COMPLETE) {
133 user_gss_error(maj_stat, min_stat, "parsing name");
134 (void) fprintf(stderr, "name parsed <%s>\n", stbuf);
135 continue;
136 }
137
138 token_ptr = GSS_C_NO_BUFFER;
139 gcontext = GSS_C_NO_CONTEXT; /* structure copy */
140
141 do {
142 if (debug)
143 (void) fprintf(stderr,
144 "calling gss_init_sec_context\n");
145
146 if (mechstr && !mechoid &&
147 __gss_mech_to_oid(mechstr, (gss_OID*)&mechoid) !=
148 GSS_S_COMPLETE)
149 (void) printf("do_auth: %s: not a valid "
150 "security mechanism\n", mechstr);
151
152 if (!mechoid)
153 mechoid = GSS_C_NULL_OID;
154
155 maj_stat = gss_init_sec_context(&min_stat,
156 GSS_C_NO_CREDENTIAL,
157 &gcontext,
158 target_name,
159 mechoid,
160 req_flags,
161 0,
162 &chan, /* channel bindings */
163 token_ptr,
164 NULL, /* ignore mech type */
165 &send_tok,
166 NULL, /* ignore ret_flags */
167 NULL); /* ignore time_rec */
168
169 if (maj_stat != GSS_S_COMPLETE &&
170 maj_stat != GSS_S_CONTINUE_NEEDED) {
171
172 /* return an error if this is NOT the ftp ticket */
173 if (strcmp(gss_trials[trial], "ftp"))
174 user_gss_error(maj_stat, min_stat,
175 "initializing context");
176
177 (void) gss_release_name(&min_stat, &target_name);
178 /* could just be that we missed on the service name */
179 goto outer_loop;
180
181 }
182
183 if (send_tok.length != 0) {
184 int len = send_tok.length;
185 reply_parse = "ADAT="; /* for command() later */
186 oldverbose = verbose;
187 verbose = (trial == n_gss_trials-1)?0:-1;
188
189 outlen = ENCODELEN(send_tok.length);
190 out_buf = (uchar_t *)malloc(outlen);
191 if (out_buf == NULL) {
192 (void) fprintf(stderr, "memory error allocating "
193 "auth buffer\n");
194 maj_stat = GSS_S_FAILURE;
195 goto outer_loop;
196 }
197 auth_error = radix_encode(send_tok.value, out_buf,
198 outlen, &len, 0);
199
200 if (auth_error) {
201 (void) fprintf(stderr, "Base 64 encoding failed: %s\n",
202 radix_error(auth_error));
203 } else if ((comcode = command("ADAT %s", out_buf))
204 != COMPLETE /* && comcode != 3 (335)*/) {
205
206 if (trial == n_gss_trials-1) {
207 (void) fprintf(stderr, "GSSAPI ADAT failed (%d)\n",
208 comcode);
209
210 /* force out of loop */
211 maj_stat = GSS_S_FAILURE;
212 }
213
214 /*
215 * backoff to the v1 gssapi is still possible.
216 * Send a new AUTH command. If that fails,
217 * terminate the loop
218 */
219 if (command("AUTH %s", "GSSAPI") != CONTINUE) {
220 (void) fprintf(stderr,
221 "GSSAPI ADAT failed, AUTH restart failed\n");
222 /* force out of loop */
223 maj_stat = GSS_S_FAILURE;
224 }
225
226 goto outer_loop;
227 } else if (!reply_parse) {
228 (void) fprintf(stderr,
229 "No authentication data received from server\n");
230 if (maj_stat == GSS_S_COMPLETE) {
231 (void) fprintf(stderr,
232 "...but no more was needed\n");
233 goto gss_complete_loop;
234 } else {
235 user_gss_error(maj_stat, min_stat, "no reply.");
236 goto gss_complete_loop;
237 }
238 } else if (auth_error = radix_encode((uchar_t *)
239 reply_parse, out_buf, outlen, &i, 1)) {
240 (void) fprintf(stderr,
241 "Base 64 decoding failed: %s\n",
242 radix_error(auth_error));
243 } else {
244 /* everything worked */
245 token_ptr = &recv_tok;
246 recv_tok.value = out_buf;
247 recv_tok.length = i;
248 continue;
249 } /* end if (auth_error) */
250
251 /* get out of loop clean */
252 gss_complete_loop:
253 trial = n_gss_trials-1;
254 gss_release_buffer(&min_stat, &send_tok);
255 gss_release_name(&min_stat, &target_name);
256 goto outer_loop;
257 } /* end if (send_tok.length != 0) */
258
259 } while (maj_stat == GSS_S_CONTINUE_NEEDED);
260
261 outer_loop:
262 if (maj_stat == GSS_S_COMPLETE)
263 break;
264
265 } /* end for loop */
266
267 verbose = oldverbose;
268 if (out_buf != NULL)
269 free(out_buf);
270
271 if (maj_stat == GSS_S_COMPLETE) {
272 (void) printf("GSSAPI authentication succeeded\n");
273 reply_parse = NULL;
274 auth_type = AUTHTYPE_GSSAPI;
275 return (1);
276 } else {
277 (void) fprintf(stderr, "GSSAPI authentication failed\n");
278 reply_parse = NULL;
279 }
280 } /* end if (command...) */
281
282 /* Other auth types go here ... */
283
284 return (0);
285 }
286
287 /*
288 * Get the information for the channel structure.
289 */
290 void
get_inet_addr_info(struct sockaddr_in6 * in_ipaddr,gss_buffer_t in_buffer)291 get_inet_addr_info(struct sockaddr_in6 *in_ipaddr, gss_buffer_t in_buffer)
292 {
293 size_t length;
294 char *value;
295
296 if (in_ipaddr == NULL) {
297 in_buffer->length = 0;
298 in_buffer->value = NULL;
299 return;
300 }
301
302 /* get the initiator address.value and address.length */
303
304 if (in_ipaddr->sin6_family == AF_INET6) {
305 struct in_addr in_ipv4addr;
306 struct sockaddr_in6 *sin6 =
307 (struct sockaddr_in6 *)in_ipaddr;
308 if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
309 IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
310 &in_ipv4addr);
311 in_buffer->length = length = sizeof (struct in_addr);
312 in_buffer->value = value = malloc(length);
313 memcpy(value, &in_ipv4addr, length);
314 } else {
315 in_buffer->length = length = sizeof (struct in6_addr);
316 in_buffer->value = value = malloc(length);
317 memcpy(value, &(sin6->sin6_addr.s6_addr),
318 length);
319 }
320 } else {
321 in_buffer->length = length = sizeof (struct in_addr);
322 in_buffer->value = value = malloc(in_buffer->length);
323 memcpy(value,
324 &((struct sockaddr_in *)(in_ipaddr))->sin_addr,
325 length);
326 }
327 }
328
329 int
radix_encode(uchar_t * inbuf,uchar_t * outbuf,size_t buflen,int * outlen,int decode)330 radix_encode(uchar_t *inbuf, uchar_t *outbuf, size_t buflen,
331 int *outlen, int decode)
332 {
333 int i, j, D;
334 char *p;
335 uchar_t c;
336
337 if (decode) {
338 for (i = j = 0;
339 inbuf[i] && inbuf[i] != radix_pad && (j < buflen);
340 i++) {
341 if ((p = strchr(radixN, inbuf[i])) == NULL)
342 return (1);
343 D = p - radixN;
344 switch (i&3) {
345 case 0:
346 outbuf[j] = D<<2;
347 break;
348 case 1:
349 outbuf[j++] |= D>>4;
350 outbuf[j] = (D&15)<<4;
351 break;
352 case 2:
353 outbuf[j++] |= D>>2;
354 outbuf[j] = (D&3)<<6;
355 break;
356 case 3:
357 outbuf[j++] |= D;
358 }
359 }
360 if (j == buflen && (inbuf[i] && inbuf[i] != radix_pad)) {
361 return (4);
362 }
363 switch (i&3) {
364 case 1: return (3);
365 case 2: if (D&15)
366 return (3);
367 if (strcmp((char *)&inbuf[i], "=="))
368 return (2);
369 break;
370 case 3: if (D&3)
371 return (3);
372 if (strcmp((char *)&inbuf[i], "="))
373 return (2);
374 }
375 *outlen = j;
376 } else {
377 for (i = j = 0; i < *outlen && j < buflen; i++)
378 switch (i%3) {
379 case 0:
380 outbuf[j++] = radixN[inbuf[i]>>2];
381 c = (inbuf[i]&3)<<4;
382 break;
383 case 1:
384 outbuf[j++] = radixN[c|inbuf[i]>>4];
385 c = (inbuf[i]&15)<<2;
386 break;
387 case 2:
388 outbuf[j++] = radixN[c|inbuf[i]>>6];
389 outbuf[j++] = radixN[inbuf[i]&63];
390 c = 0;
391 }
392 if (j == buflen && i < *outlen) {
393 return (4);
394 }
395 if (i%3)
396 outbuf[j++] = radixN[c];
397 switch (i%3) {
398 case 1:
399 outbuf[j++] = radix_pad;
400 /* FALLTHROUGH */
401 case 2:
402 outbuf[j++] = radix_pad;
403 break;
404 }
405 outbuf[*outlen = j] = '\0';
406 }
407 return (0);
408 }
409
410 char *
radix_error(int e)411 radix_error(int e)
412 {
413 switch (e) {
414 case 0: return ("Success");
415 case 1: return ("Bad character in encoding");
416 case 2: return ("Encoding not properly padded");
417 case 3: return ("Decoded # of bits not a multiple of 8");
418 case 4: return ("Buffer size error");
419 default: return ("Unknown error");
420 }
421 }
422