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
46int	auth_type;	/* Authentication succeeded?  If so, what type? */
47
48char	*radix_error(int);
49static void get_inet_addr_info(struct sockaddr_in6 *, gss_buffer_t);
50
51static char *radixN =
52	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
53static 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
63gss_OID		mechoid;
64gss_ctx_id_t	gcontext;	/* global gss security context */
65static		const char *gss_trials[] = { "ftp", "host" };
66/* the number of elements in gss_trials array */
67static const	int n_gss_trials = sizeof (gss_trials)/sizeof (char *);
68char		*reply_parse;
69
70int
71do_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 */
254gss_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
263outer_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 */
292void
293get_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
331int
332radix_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
412char *
413radix_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