/* This source code was modified by Martin Hedenfalk for * use in Curl. His latest changes were done 2000-09-18. * * It has since been patched away like a madman by Daniel Stenberg * to make it better applied to curl conditions, and to make * it not use globals, pollute name space and more. This source code awaits a * rewrite to work around the paragraph 2 in the BSD licenses as explained * below. * * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * 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. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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 "setup.h" #ifndef CURL_DISABLE_FTP #ifdef HAVE_KRB4 #include "security.h" #include "base64.h" #include #ifdef HAVE_NETDB_H #include #endif #include #include #include #ifdef HAVE_UNISTD_H #include /* for getpid() */ #endif #include "ftp.h" #include "sendf.h" #include "krb4.h" #include "memory.h" #if defined( HAVE_INET_NTOA_R ) && !defined( HAVE_INET_NTOA_R_DECL ) #include "inet_ntoa_r.h" #endif /* The last #include file should be: */ #include "memdebug.h" #define LOCAL_ADDR ( &conn->local_addr ) #define REMOTE_ADDR conn->ip_addr->ai_addr #define myctladdr LOCAL_ADDR #define hisctladdr REMOTE_ADDR struct krb4_data { des_cblock key; des_key_schedule schedule; char name[ANAME_SZ]; char instance[INST_SZ]; char realm[REALM_SZ]; }; #ifndef HAVE_STRLCPY /* if it ever goes non-static, make it Curl_ prefixed! */ static size_t strlcpy( char *dst, const char *src, size_t dst_sz ) { size_t n; char *p; for ( p = dst, n = 0; n + 1 < dst_sz && *src != '\0'; ++p, ++src, ++n ) *p = *src; *p = '\0'; if ( *src == '\0' ) { return n; } else { return n + strlen( src ); } } #else size_t strlcpy( char *dst, const char *src, size_t dst_sz ); #endif static int krb4_check_prot( void *app_data, int level ) { app_data = NULL; /* prevent compiler warning */ if ( level == prot_confidential ) { return -1; } return 0; } static int krb4_decode( void *app_data, void *buf, int len, int level, struct connectdata *conn ) { MSG_DAT m; int e; struct krb4_data *d = app_data; if ( level == prot_safe ) { e = krb_rd_safe( buf, len, &d->key, (struct sockaddr_in *)REMOTE_ADDR, (struct sockaddr_in *)LOCAL_ADDR, &m ); } else { e = krb_rd_priv( buf, len, d->schedule, &d->key, (struct sockaddr_in *)REMOTE_ADDR, (struct sockaddr_in *)LOCAL_ADDR, &m ); } if ( e ) { struct SessionHandle *data = conn->data; infof( data, "krb4_decode: %s\n", krb_get_err_text( e ) ); return -1; } memmove( buf, m.app_data, m.app_length ); return m.app_length; } static int krb4_overhead( void *app_data, int level, int len ) { /* no arguments are used, just init them to prevent compiler warnings */ app_data = NULL; level = 0; len = 0; return 31; } static int krb4_encode( void *app_data, void *from, int length, int level, void **to, struct connectdata *conn ) { struct krb4_data *d = app_data; *to = malloc( length + 31 ); if ( level == prot_safe ) { return krb_mk_safe( from, *to, length, &d->key, (struct sockaddr_in *)LOCAL_ADDR, (struct sockaddr_in *)REMOTE_ADDR ); } else if ( level == prot_private ) { return krb_mk_priv( from, *to, length, d->schedule, &d->key, (struct sockaddr_in *)LOCAL_ADDR, (struct sockaddr_in *)REMOTE_ADDR ); } else { return -1; } } static int mk_auth( struct krb4_data *d, KTEXT adat, const char *service, char *host, int checksum ) { int ret; CREDENTIALS cred; char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ]; strlcpy( sname, service, sizeof( sname ) ); strlcpy( inst, krb_get_phost( host ), sizeof( inst ) ); strlcpy( realm, krb_realmofhost( host ), sizeof( realm ) ); ret = krb_mk_req( adat, sname, inst, realm, checksum ); if ( ret ) { return ret; } strlcpy( sname, service, sizeof( sname ) ); strlcpy( inst, krb_get_phost( host ), sizeof( inst ) ); strlcpy( realm, krb_realmofhost( host ), sizeof( realm ) ); ret = krb_get_cred( sname, inst, realm, &cred ); memmove( &d->key, &cred.session, sizeof( des_cblock ) ); des_key_sched( &d->key, d->schedule ); memset( &cred, 0, sizeof( cred ) ); return ret; } #ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM int krb_get_our_ip_for_realm( char *, struct in_addr * ); #endif static int krb4_auth( void *app_data, struct connectdata *conn ) { int ret; char *p; int len; KTEXT_ST adat; MSG_DAT msg_data; int checksum; u_int32_t cs; struct krb4_data *d = app_data; char *host = conn->host.name; ssize_t nread; int l = sizeof( conn->local_addr ); struct SessionHandle *data = conn->data; CURLcode result; if ( getsockname( conn->sock[FIRSTSOCKET], (struct sockaddr *)LOCAL_ADDR, &l ) < 0 ) { perror( "getsockname()" ); } checksum = getpid(); ret = mk_auth( d, &adat, "ftp", host, checksum ); if ( ret == KDC_PR_UNKNOWN ) { ret = mk_auth( d, &adat, "rcmd", host, checksum ); } if ( ret ) { Curl_infof( data, "%s\n", krb_get_err_text( ret ) ); return AUTH_CONTINUE; } #ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM if ( krb_get_config_bool( "nat_in_use" ) ) { struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR; struct in_addr natAddr; if ( krb_get_our_ip_for_realm( krb_realmofhost( host ), &natAddr ) != KSUCCESS && krb_get_our_ip_for_realm( NULL, &natAddr ) != KSUCCESS ) { Curl_infof( data, "Can't get address for realm %s\n", krb_realmofhost( host ) ); } else { if ( natAddr.s_addr != localaddr->sin_addr.s_addr ) { #ifdef HAVE_INET_NTOA_R char ntoa_buf[64]; char *ip = (char *)inet_ntoa_r( natAddr, ntoa_buf, sizeof( ntoa_buf ) ); #else char *ip = (char *)inet_ntoa( natAddr ); #endif Curl_infof( data, "Using NAT IP address (%s) for kerberos 4\n", ip ); localaddr->sin_addr = natAddr; } } } #endif if ( Curl_base64_encode( (char *)adat.dat, adat.length, &p ) < 1 ) { Curl_failf( data, "Out of memory base64-encoding" ); return AUTH_CONTINUE; } result = Curl_ftpsendf( conn, "ADAT %s", p ); free( p ); if ( result ) { return -2; } if ( Curl_GetFTPResponse( &nread, conn, NULL ) ) { return -1; } if ( data->state.buffer[0] != '2' ) { Curl_failf( data, "Server didn't accept auth data" ); return AUTH_ERROR; } p = strstr( data->state.buffer, "ADAT=" ); if ( !p ) { Curl_failf( data, "Remote host didn't send adat reply" ); return AUTH_ERROR; } p += 5; len = Curl_base64_decode( p, (char *)adat.dat ); if ( len < 0 ) { Curl_failf( data, "Failed to decode base64 from server" ); return AUTH_ERROR; } adat.length = len; ret = krb_rd_safe( adat.dat, adat.length, &d->key, (struct sockaddr_in *)hisctladdr, (struct sockaddr_in *)myctladdr, &msg_data ); if ( ret ) { Curl_failf( data, "Error reading reply from server: %s", krb_get_err_text( ret ) ); return AUTH_ERROR; } krb_get_int( msg_data.app_data, &cs, 4, 0 ); if ( cs - checksum != 1 ) { Curl_failf( data, "Bad checksum returned from server" ); return AUTH_ERROR; } return AUTH_OK; } struct Curl_sec_client_mech Curl_krb4_client_mech = { "KERBEROS_V4", sizeof( struct krb4_data ), NULL, /* init */ krb4_auth, NULL, /* end */ krb4_check_prot, krb4_overhead, krb4_encode, krb4_decode }; CURLcode Curl_krb_kauth( struct connectdata *conn ) { des_cblock key; des_key_schedule schedule; KTEXT_ST tkt, tktcopy; char *name; char *p; char passwd[100]; int tmp; ssize_t nread; int save; CURLcode result; save = Curl_set_command_prot( conn, prot_private ); result = Curl_ftpsendf( conn, "SITE KAUTH %s", conn->user ); if ( result ) { return result; } result = Curl_GetFTPResponse( &nread, conn, NULL ); if ( result ) { return result; } if ( conn->data->state.buffer[0] != '3' ) { Curl_set_command_prot( conn, save ); return CURLE_FTP_WEIRD_SERVER_REPLY; } p = strstr( conn->data->state.buffer, "T=" ); if ( !p ) { Curl_failf( conn->data, "Bad reply from server" ); Curl_set_command_prot( conn, save ); return CURLE_FTP_WEIRD_SERVER_REPLY; } p += 2; tmp = Curl_base64_decode( p, (char *)tkt.dat ); if ( tmp < 0 ) { Curl_failf( conn->data, "Failed to decode base64 in reply.\n" ); Curl_set_command_prot( conn, save ); return CURLE_FTP_WEIRD_SERVER_REPLY; } tkt.length = tmp; tktcopy.length = tkt.length; p = strstr( conn->data->state.buffer, "P=" ); if ( !p ) { Curl_failf( conn->data, "Bad reply from server" ); Curl_set_command_prot( conn, save ); return CURLE_FTP_WEIRD_SERVER_REPLY; } name = p + 2; for (; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++ ) ; *p = 0; des_string_to_key( conn->passwd, &key ); des_key_sched( &key, schedule ); des_pcbc_encrypt( (void *)tkt.dat, (void *)tktcopy.dat, tkt.length, schedule, &key, DES_DECRYPT ); if ( strcmp( (char*)tktcopy.dat + 8, KRB_TICKET_GRANTING_TICKET ) != 0 ) { afs_string_to_key( passwd, krb_realmofhost( conn->host.name ), &key ); des_key_sched( &key, schedule ); des_pcbc_encrypt( (void *)tkt.dat, (void *)tktcopy.dat, tkt.length, schedule, &key, DES_DECRYPT ); } memset( key, 0, sizeof( key ) ); memset( schedule, 0, sizeof( schedule ) ); memset( passwd, 0, sizeof( passwd ) ); if ( Curl_base64_encode( (char *)tktcopy.dat, tktcopy.length, &p ) < 1 ) { failf( conn->data, "Out of memory base64-encoding." ); Curl_set_command_prot( conn, save ); return CURLE_OUT_OF_MEMORY; } memset( tktcopy.dat, 0, tktcopy.length ); result = Curl_ftpsendf( conn, "SITE KAUTH %s %s", name, p ); free( p ); if ( result ) { return result; } result = Curl_GetFTPResponse( &nread, conn, NULL ); if ( result ) { return result; } Curl_set_command_prot( conn, save ); return CURLE_OK; } #endif /* HAVE_KRB4 */ #endif /* CURL_DISABLE_FTP */