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