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