/* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1985, 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "ftp_var.h" #include #include #include int auth_type; /* Authentication succeeded? If so, what type? */ char *radix_error(int); static void get_inet_addr_info(struct sockaddr_in6 *, gss_buffer_t); static char *radixN = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static char radix_pad = '='; /* * authenticate the user, if auth_type is AUTHTYPE_NONE * * Returns: 0 if there is no auth type * 1 if success * 2 if failure */ gss_OID mechoid; gss_ctx_id_t gcontext; /* global gss security context */ static const char *gss_trials[] = { "ftp", "host" }; /* the number of elements in gss_trials array */ static const int n_gss_trials = sizeof (gss_trials)/sizeof (char *); char *reply_parse; int do_auth(void) { int oldverbose = verbose; uchar_t *out_buf = NULL; size_t outlen; int i; if (auth_type != AUTHTYPE_NONE) return (1); /* auth already succeeded */ /* Other auth types go here ... */ if (command("AUTH %s", "GSSAPI") == CONTINUE) { OM_uint32 maj_stat, min_stat; gss_name_t target_name; gss_buffer_desc send_tok, recv_tok, *token_ptr; gss_buffer_desc temp_buf; char stbuf[FTPBUFSIZ]; int comcode, trial; int req_flags; struct gss_channel_bindings_struct chan; get_inet_addr_info(&myctladdr, &temp_buf); chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32 */ chan.initiator_address.length = temp_buf.length; chan.initiator_address.value = malloc(temp_buf.length); memcpy(chan.initiator_address.value, temp_buf.value, temp_buf.length); get_inet_addr_info(&remctladdr, &temp_buf); chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */ chan.acceptor_address.length = temp_buf.length; chan.acceptor_address.value = malloc(temp_buf.length); memcpy(chan.acceptor_address.value, temp_buf.value, temp_buf.length); chan.application_data.length = 0; chan.application_data.value = 0; if (verbose) (void) printf("GSSAPI accepted as authentication type\n"); /* set the forward flag */ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; if (fflag) req_flags |= GSS_C_DELEG_FLAG; /* blob from gss-client */ for (trial = 0; trial < n_gss_trials; trial++) { /* ftp@hostname first, then host@hostname */ /* the V5 GSSAPI binding canonicalizes this for us... */ (void) snprintf(stbuf, FTPBUFSIZ, "%s@%s", gss_trials[trial], hostname); if (debug) (void) fprintf(stderr, "Trying to authenticate to <%s>\n", stbuf); send_tok.value = stbuf; send_tok.length = strlen(stbuf) + 1; maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NT_HOSTBASED_SERVICE, &target_name); if (maj_stat != GSS_S_COMPLETE) { user_gss_error(maj_stat, min_stat, "parsing name"); (void) fprintf(stderr, "name parsed <%s>\n", stbuf); continue; } token_ptr = GSS_C_NO_BUFFER; gcontext = GSS_C_NO_CONTEXT; /* structure copy */ do { if (debug) (void) fprintf(stderr, "calling gss_init_sec_context\n"); if (mechstr && !mechoid && __gss_mech_to_oid(mechstr, (gss_OID*)&mechoid) != GSS_S_COMPLETE) (void) printf("do_auth: %s: not a valid " "security mechanism\n", mechstr); if (!mechoid) mechoid = GSS_C_NULL_OID; maj_stat = gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, &gcontext, target_name, mechoid, req_flags, 0, &chan, /* channel bindings */ token_ptr, NULL, /* ignore mech type */ &send_tok, NULL, /* ignore ret_flags */ NULL); /* ignore time_rec */ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) { /* return an error if this is NOT the ftp ticket */ if (strcmp(gss_trials[trial], "ftp")) user_gss_error(maj_stat, min_stat, "initializing context"); (void) gss_release_name(&min_stat, &target_name); /* could just be that we missed on the service name */ goto outer_loop; } if (send_tok.length != 0) { int len = send_tok.length; reply_parse = "ADAT="; /* for command() later */ oldverbose = verbose; verbose = (trial == n_gss_trials-1)?0:-1; outlen = ENCODELEN(send_tok.length); out_buf = (uchar_t *)malloc(outlen); if (out_buf == NULL) { (void) fprintf(stderr, "memory error allocating " "auth buffer\n"); maj_stat = GSS_S_FAILURE; goto outer_loop; } auth_error = radix_encode(send_tok.value, out_buf, outlen, &len, 0); if (auth_error) { (void) fprintf(stderr, "Base 64 encoding failed: %s\n", radix_error(auth_error)); } else if ((comcode = command("ADAT %s", out_buf)) != COMPLETE /* && comcode != 3 (335)*/) { if (trial == n_gss_trials-1) { (void) fprintf(stderr, "GSSAPI ADAT failed (%d)\n", comcode); /* force out of loop */ maj_stat = GSS_S_FAILURE; } /* * backoff to the v1 gssapi is still possible. * Send a new AUTH command. If that fails, * terminate the loop */ if (command("AUTH %s", "GSSAPI") != CONTINUE) { (void) fprintf(stderr, "GSSAPI ADAT failed, AUTH restart failed\n"); /* force out of loop */ maj_stat = GSS_S_FAILURE; } goto outer_loop; } else if (!reply_parse) { (void) fprintf(stderr, "No authentication data received from server\n"); if (maj_stat == GSS_S_COMPLETE) { (void) fprintf(stderr, "...but no more was needed\n"); goto gss_complete_loop; } else { user_gss_error(maj_stat, min_stat, "no reply."); goto gss_complete_loop; } } else if (auth_error = radix_encode((uchar_t *) reply_parse, out_buf, outlen, &i, 1)) { (void) fprintf(stderr, "Base 64 decoding failed: %s\n", radix_error(auth_error)); } else { /* everything worked */ token_ptr = &recv_tok; recv_tok.value = out_buf; recv_tok.length = i; continue; } /* end if (auth_error) */ /* get out of loop clean */ gss_complete_loop: trial = n_gss_trials-1; gss_release_buffer(&min_stat, &send_tok); gss_release_name(&min_stat, &target_name); goto outer_loop; } /* end if (send_tok.length != 0) */ } while (maj_stat == GSS_S_CONTINUE_NEEDED); outer_loop: if (maj_stat == GSS_S_COMPLETE) break; } /* end for loop */ verbose = oldverbose; if (out_buf != NULL) free(out_buf); if (maj_stat == GSS_S_COMPLETE) { (void) printf("GSSAPI authentication succeeded\n"); reply_parse = NULL; auth_type = AUTHTYPE_GSSAPI; return (1); } else { (void) fprintf(stderr, "GSSAPI authentication failed\n"); reply_parse = NULL; } } /* end if (command...) */ /* Other auth types go here ... */ return (0); } /* * Get the information for the channel structure. */ void get_inet_addr_info(struct sockaddr_in6 *in_ipaddr, gss_buffer_t in_buffer) { size_t length; char *value; if (in_ipaddr == NULL) { in_buffer->length = 0; in_buffer->value = NULL; return; } /* get the initiator address.value and address.length */ if (in_ipaddr->sin6_family == AF_INET6) { struct in_addr in_ipv4addr; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)in_ipaddr; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &in_ipv4addr); in_buffer->length = length = sizeof (struct in_addr); in_buffer->value = value = malloc(length); memcpy(value, &in_ipv4addr, length); } else { in_buffer->length = length = sizeof (struct in6_addr); in_buffer->value = value = malloc(length); memcpy(value, &(sin6->sin6_addr.s6_addr), length); } } else { in_buffer->length = length = sizeof (struct in_addr); in_buffer->value = value = malloc(in_buffer->length); memcpy(value, &((struct sockaddr_in *)(in_ipaddr))->sin_addr, length); } } int radix_encode(uchar_t *inbuf, uchar_t *outbuf, size_t buflen, int *outlen, int decode) { int i, j, D; char *p; uchar_t c; if (decode) { for (i = j = 0; inbuf[i] && inbuf[i] != radix_pad && (j < buflen); i++) { if ((p = strchr(radixN, inbuf[i])) == NULL) return (1); D = p - radixN; switch (i&3) { case 0: outbuf[j] = D<<2; break; case 1: outbuf[j++] |= D>>4; outbuf[j] = (D&15)<<4; break; case 2: outbuf[j++] |= D>>2; outbuf[j] = (D&3)<<6; break; case 3: outbuf[j++] |= D; } } if (j == buflen && (inbuf[i] && inbuf[i] != radix_pad)) { return (4); } switch (i&3) { case 1: return (3); case 2: if (D&15) return (3); if (strcmp((char *)&inbuf[i], "==")) return (2); break; case 3: if (D&3) return (3); if (strcmp((char *)&inbuf[i], "=")) return (2); } *outlen = j; } else { for (i = j = 0; i < *outlen && j < buflen; i++) switch (i%3) { case 0: outbuf[j++] = radixN[inbuf[i]>>2]; c = (inbuf[i]&3)<<4; break; case 1: outbuf[j++] = radixN[c|inbuf[i]>>4]; c = (inbuf[i]&15)<<2; break; case 2: outbuf[j++] = radixN[c|inbuf[i]>>6]; outbuf[j++] = radixN[inbuf[i]&63]; c = 0; } if (j == buflen && i < *outlen) { return (4); } if (i%3) outbuf[j++] = radixN[c]; switch (i%3) { case 1: outbuf[j++] = radix_pad; /* FALLTHROUGH */ case 2: outbuf[j++] = radix_pad; break; } outbuf[*outlen = j] = '\0'; } return (0); } char * radix_error(int e) { switch (e) { case 0: return ("Success"); case 1: return ("Bad character in encoding"); case 2: return ("Encoding not properly padded"); case 3: return ("Decoded # of bits not a multiple of 8"); case 4: return ("Buffer size error"); default: return ("Unknown error"); } }