Home | History | Annotate | Download | only in ftp
      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
     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
    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
    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 *
    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