00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "curl_setup.h"
00036
00037 #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
00038
00039 #ifdef HAVE_NETDB_H
00040 #include <netdb.h>
00041 #endif
00042
00043 #include "urldata.h"
00044 #include "curl_base64.h"
00045 #include "ftp.h"
00046 #include "curl_gssapi.h"
00047 #include "sendf.h"
00048 #include "curl_sec.h"
00049 #include "warnless.h"
00050
00051
00052 #include "curl_printf.h"
00053 #include "curl_memory.h"
00054 #include "memdebug.h"
00055
00056 static int
00057 krb5_init(void *app_data)
00058 {
00059 gss_ctx_id_t *context = app_data;
00060
00061 *context = GSS_C_NO_CONTEXT;
00062 return 0;
00063 }
00064
00065 static int
00066 krb5_check_prot(void *app_data, int level)
00067 {
00068 (void)app_data;
00069 if(level == PROT_CONFIDENTIAL)
00070 return -1;
00071 return 0;
00072 }
00073
00074 static int
00075 krb5_decode(void *app_data, void *buf, int len,
00076 int level UNUSED_PARAM,
00077 struct connectdata *conn UNUSED_PARAM)
00078 {
00079 gss_ctx_id_t *context = app_data;
00080 OM_uint32 maj, min;
00081 gss_buffer_desc enc, dec;
00082
00083 (void)level;
00084 (void)conn;
00085
00086 enc.value = buf;
00087 enc.length = len;
00088 maj = gss_unseal(&min, *context, &enc, &dec, NULL, NULL);
00089 if(maj != GSS_S_COMPLETE) {
00090 if(len >= 4)
00091 strcpy(buf, "599 ");
00092 return -1;
00093 }
00094
00095 memcpy(buf, dec.value, dec.length);
00096 len = curlx_uztosi(dec.length);
00097 gss_release_buffer(&min, &dec);
00098
00099 return len;
00100 }
00101
00102 static int
00103 krb5_overhead(void *app_data, int level, int len)
00104 {
00105
00106 (void)app_data;
00107 (void)level;
00108 (void)len;
00109 return 0;
00110 }
00111
00112 static int
00113 krb5_encode(void *app_data, const void *from, int length, int level, void **to)
00114 {
00115 gss_ctx_id_t *context = app_data;
00116 gss_buffer_desc dec, enc;
00117 OM_uint32 maj, min;
00118 int state;
00119 int len;
00120
00121
00122
00123
00124 dec.value = (void *)from;
00125 dec.length = length;
00126 maj = gss_seal(&min, *context,
00127 level == PROT_PRIVATE,
00128 GSS_C_QOP_DEFAULT,
00129 &dec, &state, &enc);
00130
00131 if(maj != GSS_S_COMPLETE)
00132 return -1;
00133
00134
00135
00136 *to = malloc(enc.length);
00137 if(!*to)
00138 return -1;
00139 memcpy(*to, enc.value, enc.length);
00140 len = curlx_uztosi(enc.length);
00141 gss_release_buffer(&min, &enc);
00142 return len;
00143 }
00144
00145 static int
00146 krb5_auth(void *app_data, struct connectdata *conn)
00147 {
00148 int ret = AUTH_OK;
00149 char *p;
00150 const char *host = conn->host.name;
00151 ssize_t nread;
00152 curl_socklen_t l = sizeof(conn->local_addr);
00153 struct Curl_easy *data = conn->data;
00154 CURLcode result;
00155 const char *service = data->set.str[STRING_SERVICE_NAME] ?
00156 data->set.str[STRING_SERVICE_NAME] :
00157 "ftp";
00158 const char *srv_host = "host";
00159 gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
00160 OM_uint32 maj, min;
00161 gss_name_t gssname;
00162 gss_ctx_id_t *context = app_data;
00163 struct gss_channel_bindings_struct chan;
00164 size_t base64_sz = 0;
00165 struct sockaddr_in **remote_addr =
00166 (struct sockaddr_in **)&conn->ip_addr->ai_addr;
00167
00168 if(getsockname(conn->sock[FIRSTSOCKET],
00169 (struct sockaddr *)&conn->local_addr, &l) < 0)
00170 perror("getsockname()");
00171
00172 chan.initiator_addrtype = GSS_C_AF_INET;
00173 chan.initiator_address.length = l - 4;
00174 chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
00175 chan.acceptor_addrtype = GSS_C_AF_INET;
00176 chan.acceptor_address.length = l - 4;
00177 chan.acceptor_address.value = &(*remote_addr)->sin_addr.s_addr;
00178 chan.application_data.length = 0;
00179 chan.application_data.value = NULL;
00180
00181
00182 for(;;) {
00183
00184 if(service == srv_host) {
00185 result = Curl_ftpsend(conn, "AUTH GSSAPI");
00186 if(result)
00187 return -2;
00188
00189 if(Curl_GetFTPResponse(&nread, conn, NULL))
00190 return -1;
00191
00192 if(data->state.buffer[0] != '3')
00193 return -1;
00194 }
00195
00196 input_buffer.value = data->state.buffer;
00197 input_buffer.length = snprintf(input_buffer.value, BUFSIZE, "%s@%s",
00198 service, host);
00199 maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
00200 &gssname);
00201 if(maj != GSS_S_COMPLETE) {
00202 gss_release_name(&min, &gssname);
00203 if(service == srv_host) {
00204 Curl_failf(data, "Error importing service name %s",
00205 input_buffer.value);
00206 return AUTH_ERROR;
00207 }
00208 service = srv_host;
00209 continue;
00210 }
00211
00212 gss_display_name(&min, gssname, &output_buffer, NULL);
00213 Curl_infof(data, "Trying against %s\n", output_buffer.value);
00214 gssresp = GSS_C_NO_BUFFER;
00215 *context = GSS_C_NO_CONTEXT;
00216
00217 do {
00218
00219
00220
00221 gss_release_buffer(&min, &output_buffer);
00222 ret = AUTH_OK;
00223 maj = Curl_gss_init_sec_context(data,
00224 &min,
00225 context,
00226 gssname,
00227 &Curl_krb5_mech_oid,
00228 &chan,
00229 gssresp,
00230 &output_buffer,
00231 TRUE,
00232 NULL);
00233
00234 if(gssresp) {
00235 free(_gssresp.value);
00236 gssresp = NULL;
00237 }
00238
00239 if(GSS_ERROR(maj)) {
00240 Curl_infof(data, "Error creating security context\n");
00241 ret = AUTH_ERROR;
00242 break;
00243 }
00244
00245 if(output_buffer.length != 0) {
00246 char *cmd;
00247
00248 result = Curl_base64_encode(data, (char *)output_buffer.value,
00249 output_buffer.length, &p, &base64_sz);
00250 if(result) {
00251 Curl_infof(data, "base64-encoding: %s\n",
00252 curl_easy_strerror(result));
00253 ret = AUTH_ERROR;
00254 break;
00255 }
00256
00257 cmd = aprintf("ADAT %s", p);
00258 if(cmd)
00259 result = Curl_ftpsend(conn, cmd);
00260 else
00261 result = CURLE_OUT_OF_MEMORY;
00262
00263 free(p);
00264
00265 if(result) {
00266 ret = -2;
00267 break;
00268 }
00269
00270 if(Curl_GetFTPResponse(&nread, conn, NULL)) {
00271 ret = -1;
00272 break;
00273 }
00274
00275 if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
00276 Curl_infof(data, "Server didn't accept auth data\n");
00277 ret = AUTH_ERROR;
00278 break;
00279 }
00280
00281 p = data->state.buffer + 4;
00282 p = strstr(p, "ADAT=");
00283 if(p) {
00284 result = Curl_base64_decode(p + 5,
00285 (unsigned char **)&_gssresp.value,
00286 &_gssresp.length);
00287 if(result) {
00288 Curl_failf(data, "base64-decoding: %s",
00289 curl_easy_strerror(result));
00290 ret = AUTH_CONTINUE;
00291 break;
00292 }
00293 }
00294
00295 gssresp = &_gssresp;
00296 }
00297 } while(maj == GSS_S_CONTINUE_NEEDED);
00298
00299 gss_release_name(&min, &gssname);
00300 gss_release_buffer(&min, &output_buffer);
00301
00302 if(gssresp)
00303 free(_gssresp.value);
00304
00305 if(ret == AUTH_OK || service == srv_host)
00306 return ret;
00307
00308 service = srv_host;
00309 }
00310 return ret;
00311 }
00312
00313 static void krb5_end(void *app_data)
00314 {
00315 OM_uint32 min;
00316 gss_ctx_id_t *context = app_data;
00317 if(*context != GSS_C_NO_CONTEXT) {
00318 #ifdef DEBUGBUILD
00319 OM_uint32 maj =
00320 #endif
00321 gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
00322 DEBUGASSERT(maj == GSS_S_COMPLETE);
00323 }
00324 }
00325
00326 struct Curl_sec_client_mech Curl_krb5_client_mech = {
00327 "GSSAPI",
00328 sizeof(gss_ctx_id_t),
00329 krb5_init,
00330 krb5_auth,
00331 krb5_end,
00332 krb5_check_prot,
00333 krb5_overhead,
00334 krb5_encode,
00335 krb5_decode
00336 };
00337
00338 #endif