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
00036
00037
00038
00039
00040
00041
00042
00043 #include "setup.h"
00044
00045 #ifndef CURL_DISABLE_FTP
00046 #ifdef HAVE_KRB4
00047
00048 #include <stdlib.h>
00049 #ifdef HAVE_NETDB_H
00050 #include <netdb.h>
00051 #endif
00052 #include <string.h>
00053 #include <krb.h>
00054 #include <des.h>
00055
00056 #ifdef HAVE_UNISTD_H
00057 #include <unistd.h>
00058 #endif
00059
00060 #include "urldata.h"
00061 #include "base64.h"
00062 #include "ftp.h"
00063 #include "sendf.h"
00064 #include "krb4.h"
00065 #include "memory.h"
00066
00067 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
00068 #include "inet_ntoa_r.h"
00069 #endif
00070
00071
00072 #include "memdebug.h"
00073
00074 #define LOCAL_ADDR (&conn->local_addr)
00075 #define REMOTE_ADDR conn->ip_addr->ai_addr
00076 #define myctladdr LOCAL_ADDR
00077 #define hisctladdr REMOTE_ADDR
00078
00079 struct krb4_data {
00080 des_cblock key;
00081 des_key_schedule schedule;
00082 char name[ANAME_SZ];
00083 char instance[INST_SZ];
00084 char realm[REALM_SZ];
00085 };
00086
00087 #ifndef HAVE_STRLCPY
00088
00089 static size_t
00090 strlcpy (char *dst, const char *src, size_t dst_sz)
00091 {
00092 size_t n;
00093 char *p;
00094
00095 for (p = dst, n = 0;
00096 n + 1 < dst_sz && *src != '\0';
00097 ++p, ++src, ++n)
00098 *p = *src;
00099 *p = '\0';
00100 if (*src == '\0')
00101 return n;
00102 else
00103 return n + strlen (src);
00104 }
00105 #else
00106 size_t strlcpy (char *dst, const char *src, size_t dst_sz);
00107 #endif
00108
00109 static int
00110 krb4_check_prot(void *app_data, int level)
00111 {
00112 app_data = NULL;
00113 if(level == prot_confidential)
00114 return -1;
00115 return 0;
00116 }
00117
00118 static int
00119 krb4_decode(void *app_data, void *buf, int len, int level,
00120 struct connectdata *conn)
00121 {
00122 MSG_DAT m;
00123 int e;
00124 struct krb4_data *d = app_data;
00125
00126 if(level == prot_safe)
00127 e = krb_rd_safe(buf, len, &d->key,
00128 (struct sockaddr_in *)REMOTE_ADDR,
00129 (struct sockaddr_in *)LOCAL_ADDR, &m);
00130 else
00131 e = krb_rd_priv(buf, len, d->schedule, &d->key,
00132 (struct sockaddr_in *)REMOTE_ADDR,
00133 (struct sockaddr_in *)LOCAL_ADDR, &m);
00134 if(e) {
00135 struct SessionHandle *data = conn->data;
00136 infof(data, "krb4_decode: %s\n", krb_get_err_text(e));
00137 return -1;
00138 }
00139 memmove(buf, m.app_data, m.app_length);
00140 return m.app_length;
00141 }
00142
00143 static int
00144 krb4_overhead(void *app_data, int level, int len)
00145 {
00146
00147 app_data = NULL;
00148 level = 0;
00149 len = 0;
00150 return 31;
00151 }
00152
00153 static int
00154 krb4_encode(void *app_data, void *from, int length, int level, void **to,
00155 struct connectdata *conn)
00156 {
00157 struct krb4_data *d = app_data;
00158 *to = malloc(length + 31);
00159 if(level == prot_safe)
00160 return krb_mk_safe(from, *to, length, &d->key,
00161 (struct sockaddr_in *)LOCAL_ADDR,
00162 (struct sockaddr_in *)REMOTE_ADDR);
00163 else if(level == prot_private)
00164 return krb_mk_priv(from, *to, length, d->schedule, &d->key,
00165 (struct sockaddr_in *)LOCAL_ADDR,
00166 (struct sockaddr_in *)REMOTE_ADDR);
00167 else
00168 return -1;
00169 }
00170
00171 static int
00172 mk_auth(struct krb4_data *d, KTEXT adat,
00173 const char *service, char *host, int checksum)
00174 {
00175 int ret;
00176 CREDENTIALS cred;
00177 char sname[SNAME_SZ], inst[INST_SZ], realm[REALM_SZ];
00178
00179 strlcpy(sname, service, sizeof(sname));
00180 strlcpy(inst, krb_get_phost(host), sizeof(inst));
00181 strlcpy(realm, krb_realmofhost(host), sizeof(realm));
00182 ret = krb_mk_req(adat, sname, inst, realm, checksum);
00183 if(ret)
00184 return ret;
00185 strlcpy(sname, service, sizeof(sname));
00186 strlcpy(inst, krb_get_phost(host), sizeof(inst));
00187 strlcpy(realm, krb_realmofhost(host), sizeof(realm));
00188 ret = krb_get_cred(sname, inst, realm, &cred);
00189 memmove(&d->key, &cred.session, sizeof(des_cblock));
00190 des_key_sched(&d->key, d->schedule);
00191 memset(&cred, 0, sizeof(cred));
00192 return ret;
00193 }
00194
00195 #ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
00196 int krb_get_our_ip_for_realm(char *, struct in_addr *);
00197 #endif
00198
00199 static int
00200 krb4_auth(void *app_data, struct connectdata *conn)
00201 {
00202 int ret;
00203 char *p;
00204 unsigned char *ptr;
00205 size_t len;
00206 KTEXT_ST adat;
00207 MSG_DAT msg_data;
00208 int checksum;
00209 u_int32_t cs;
00210 struct krb4_data *d = app_data;
00211 char *host = conn->host.name;
00212 ssize_t nread;
00213 int l = sizeof(conn->local_addr);
00214 struct SessionHandle *data = conn->data;
00215 CURLcode result;
00216
00217 if(getsockname(conn->sock[FIRSTSOCKET],
00218 (struct sockaddr *)LOCAL_ADDR, &l) < 0)
00219 perror("getsockname()");
00220
00221 checksum = getpid();
00222 ret = mk_auth(d, &adat, "ftp", host, checksum);
00223 if(ret == KDC_PR_UNKNOWN)
00224 ret = mk_auth(d, &adat, "rcmd", host, checksum);
00225 if(ret) {
00226 infof(data, "%s\n", krb_get_err_text(ret));
00227 return AUTH_CONTINUE;
00228 }
00229
00230 #ifdef HAVE_KRB_GET_OUR_IP_FOR_REALM
00231 if (krb_get_config_bool("nat_in_use")) {
00232 struct sockaddr_in *localaddr = (struct sockaddr_in *)LOCAL_ADDR;
00233 struct in_addr natAddr;
00234
00235 if (krb_get_our_ip_for_realm(krb_realmofhost(host),
00236 &natAddr) != KSUCCESS
00237 && krb_get_our_ip_for_realm(NULL, &natAddr) != KSUCCESS)
00238 infof(data, "Can't get address for realm %s\n",
00239 krb_realmofhost(host));
00240 else {
00241 if (natAddr.s_addr != localaddr->sin_addr.s_addr) {
00242 #ifdef HAVE_INET_NTOA_R
00243 char ntoa_buf[64];
00244 char *ip = (char *)inet_ntoa_r(natAddr, ntoa_buf, sizeof(ntoa_buf));
00245 #else
00246 char *ip = (char *)inet_ntoa(natAddr);
00247 #endif
00248 infof(data, "Using NAT IP address (%s) for kerberos 4\n", ip);
00249 localaddr->sin_addr = natAddr;
00250 }
00251 }
00252 }
00253 #endif
00254
00255 if(Curl_base64_encode(conn->data, (char *)adat.dat, adat.length, &p) < 1) {
00256 Curl_failf(data, "Out of memory base64-encoding");
00257 return AUTH_CONTINUE;
00258 }
00259
00260 result = Curl_ftpsendf(conn, "ADAT %s", p);
00261
00262 free(p);
00263
00264 if(result)
00265 return -2;
00266
00267 if(Curl_GetFTPResponse(&nread, conn, NULL))
00268 return -1;
00269
00270 if(data->state.buffer[0] != '2'){
00271 Curl_failf(data, "Server didn't accept auth data");
00272 return AUTH_ERROR;
00273 }
00274
00275 p = strstr(data->state.buffer, "ADAT=");
00276 if(!p) {
00277 Curl_failf(data, "Remote host didn't send adat reply");
00278 return AUTH_ERROR;
00279 }
00280 p += 5;
00281 len = Curl_base64_decode(p, &ptr);
00282 if(len > sizeof(adat.dat)-1) {
00283 free(ptr);
00284 len=0;
00285 }
00286 if(!len || !ptr) {
00287 Curl_failf(data, "Failed to decode base64 from server");
00288 return AUTH_ERROR;
00289 }
00290 memcpy((char *)adat.dat, ptr, len);
00291 free(ptr);
00292 adat.length = len;
00293 ret = krb_rd_safe(adat.dat, adat.length, &d->key,
00294 (struct sockaddr_in *)hisctladdr,
00295 (struct sockaddr_in *)myctladdr, &msg_data);
00296 if(ret) {
00297 Curl_failf(data, "Error reading reply from server: %s",
00298 krb_get_err_text(ret));
00299 return AUTH_ERROR;
00300 }
00301 krb_get_int(msg_data.app_data, &cs, 4, 0);
00302 if(cs - checksum != 1) {
00303 Curl_failf(data, "Bad checksum returned from server");
00304 return AUTH_ERROR;
00305 }
00306 return AUTH_OK;
00307 }
00308
00309 struct Curl_sec_client_mech Curl_krb4_client_mech = {
00310 "KERBEROS_V4",
00311 sizeof(struct krb4_data),
00312 NULL,
00313 krb4_auth,
00314 NULL,
00315 krb4_check_prot,
00316 krb4_overhead,
00317 krb4_encode,
00318 krb4_decode
00319 };
00320
00321 CURLcode Curl_krb_kauth(struct connectdata *conn)
00322 {
00323 des_cblock key;
00324 des_key_schedule schedule;
00325 KTEXT_ST tkt, tktcopy;
00326 char *name;
00327 char *p;
00328 char passwd[100];
00329 size_t tmp;
00330 ssize_t nread;
00331 int save;
00332 CURLcode result;
00333 unsigned char *ptr;
00334
00335 save = Curl_set_command_prot(conn, prot_private);
00336
00337 result = Curl_ftpsendf(conn, "SITE KAUTH %s", conn->user);
00338
00339 if(result)
00340 return result;
00341
00342 result = Curl_GetFTPResponse(&nread, conn, NULL);
00343 if(result)
00344 return result;
00345
00346 if(conn->data->state.buffer[0] != '3'){
00347 Curl_set_command_prot(conn, save);
00348 return CURLE_FTP_WEIRD_SERVER_REPLY;
00349 }
00350
00351 p = strstr(conn->data->state.buffer, "T=");
00352 if(!p) {
00353 Curl_failf(conn->data, "Bad reply from server");
00354 Curl_set_command_prot(conn, save);
00355 return CURLE_FTP_WEIRD_SERVER_REPLY;
00356 }
00357
00358 p += 2;
00359 tmp = Curl_base64_decode(p, &ptr);
00360 if(tmp >= sizeof(tkt.dat)) {
00361 free(ptr);
00362 tmp=0;
00363 }
00364 if(!tmp || !ptr) {
00365 Curl_failf(conn->data, "Failed to decode base64 in reply.\n");
00366 Curl_set_command_prot(conn, save);
00367 return CURLE_FTP_WEIRD_SERVER_REPLY;
00368 }
00369 memcpy((char *)tkt.dat, ptr, tmp);
00370 free(ptr);
00371 tkt.length = tmp;
00372 tktcopy.length = tkt.length;
00373
00374 p = strstr(conn->data->state.buffer, "P=");
00375 if(!p) {
00376 Curl_failf(conn->data, "Bad reply from server");
00377 Curl_set_command_prot(conn, save);
00378 return CURLE_FTP_WEIRD_SERVER_REPLY;
00379 }
00380 name = p + 2;
00381 for(; *p && *p != ' ' && *p != '\r' && *p != '\n'; p++);
00382 *p = 0;
00383
00384 des_string_to_key (conn->passwd, &key);
00385 des_key_sched(&key, schedule);
00386
00387 des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
00388 tkt.length,
00389 schedule, &key, DES_DECRYPT);
00390 if (strcmp ((char*)tktcopy.dat + 8,
00391 KRB_TICKET_GRANTING_TICKET) != 0) {
00392 afs_string_to_key(passwd,
00393 krb_realmofhost(conn->host.name),
00394 &key);
00395 des_key_sched(&key, schedule);
00396 des_pcbc_encrypt((void *)tkt.dat, (void *)tktcopy.dat,
00397 tkt.length,
00398 schedule, &key, DES_DECRYPT);
00399 }
00400 memset(key, 0, sizeof(key));
00401 memset(schedule, 0, sizeof(schedule));
00402 memset(passwd, 0, sizeof(passwd));
00403 if(Curl_base64_encode(conn->data, (char *)tktcopy.dat, tktcopy.length, &p)
00404 < 1) {
00405 failf(conn->data, "Out of memory base64-encoding.");
00406 Curl_set_command_prot(conn, save);
00407 return CURLE_OUT_OF_MEMORY;
00408 }
00409 memset (tktcopy.dat, 0, tktcopy.length);
00410
00411 result = Curl_ftpsendf(conn, "SITE KAUTH %s %s", name, p);
00412 free(p);
00413 if(result)
00414 return result;
00415
00416 result = Curl_GetFTPResponse(&nread, conn, NULL);
00417 if(result)
00418 return result;
00419 Curl_set_command_prot(conn, save);
00420
00421 return CURLE_OK;
00422 }
00423
00424 #endif
00425 #endif