1/* GSSAPI/krb5 support for FTP - loosely based on old krb4.c
2 *
3 * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan
4 * (Royal Institute of Technology, Stockholm, Sweden).
5 * Copyright (C) Daniel Stenberg
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: BSD-3-Clause
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
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 *
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE. */
36
37#include "curl_setup.h"
38
39#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
40
41#ifdef HAVE_NETDB_H
42#include <netdb.h>
43#endif
44#ifdef HAVE_ARPA_INET_H
45#include <arpa/inet.h>
46#endif
47
48#include "urldata.h"
49#include "cfilters.h"
50#include "cf-socket.h"
51#include "curl_base64.h"
52#include "ftp.h"
53#include "curl_gssapi.h"
54#include "sendf.h"
55#include "curl_krb5.h"
56#include "warnless.h"
57#include "strcase.h"
58#include "strdup.h"
59
60/* The last 3 #include files should be in this order */
61#include "curl_printf.h"
62#include "curl_memory.h"
63#include "memdebug.h"
64
65static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
66 const char *cmd)
67{
68 ssize_t bytes_written;
69#define SBUF_SIZE 1024
70 char s[SBUF_SIZE];
71 size_t write_len;
72 char *sptr = s;
73 CURLcode result = CURLE_OK;
74#ifdef HAVE_GSSAPI
75 unsigned char data_sec = conn->data_prot;
76#endif
77
78 if(!cmd)
79 return CURLE_BAD_FUNCTION_ARGUMENT;
80
81 write_len = strlen(cmd);
82 if(!write_len || write_len > (sizeof(s) -3))
83 return CURLE_BAD_FUNCTION_ARGUMENT;
84
85 memcpy(&s, cmd, write_len);
86 strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */
87 write_len += 2;
88 bytes_written = 0;
89
90 for(;;) {
91#ifdef HAVE_GSSAPI
92 conn->data_prot = PROT_CMD;
93#endif
94 result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len,
95 &bytes_written);
96#ifdef HAVE_GSSAPI
97 DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
98 conn->data_prot = data_sec;
99#endif
100
101 if(result)
102 break;
103
104 Curl_debug(data, CURLINFO_HEADER_OUT, sptr, (size_t)bytes_written);
105
106 if(bytes_written != (ssize_t)write_len) {
107 write_len -= bytes_written;
108 sptr += bytes_written;
109 }
110 else
111 break;
112 }
113
114 return result;
115}
116
117static int
118krb5_init(void *app_data)
119{
120 gss_ctx_id_t *context = app_data;
121 /* Make sure our context is initialized for krb5_end. */
122 *context = GSS_C_NO_CONTEXT;
123 return 0;
124}
125
126static int
127krb5_check_prot(void *app_data, int level)
128{
129 (void)app_data; /* unused */
130 if(level == PROT_CONFIDENTIAL)
131 return -1;
132 return 0;
133}
134
135static int
136krb5_decode(void *app_data, void *buf, int len,
137 int level UNUSED_PARAM,
138 struct connectdata *conn UNUSED_PARAM)
139{
140 gss_ctx_id_t *context = app_data;
141 OM_uint32 maj, min;
142 gss_buffer_desc enc, dec;
143
144 (void)level;
145 (void)conn;
146
147 enc.value = buf;
148 enc.length = len;
149 maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL);
150 if(maj != GSS_S_COMPLETE)
151 return -1;
152
153 memcpy(buf, dec.value, dec.length);
154 len = curlx_uztosi(dec.length);
155 gss_release_buffer(&min, &dec);
156
157 return len;
158}
159
160static int
161krb5_encode(void *app_data, const void *from, int length, int level, void **to)
162{
163 gss_ctx_id_t *context = app_data;
164 gss_buffer_desc dec, enc;
165 OM_uint32 maj, min;
166 int state;
167 int len;
168
169 /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
170 * libraries modify the input buffer in gss_wrap()
171 */
172 dec.value = (void *)from;
173 dec.length = length;
174 maj = gss_wrap(&min, *context,
175 level == PROT_PRIVATE,
176 GSS_C_QOP_DEFAULT,
177 &dec, &state, &enc);
178
179 if(maj != GSS_S_COMPLETE)
180 return -1;
181
182 /* malloc a new buffer, in case gss_release_buffer doesn't work as
183 expected */
184 *to = malloc(enc.length);
185 if(!*to)
186 return -1;
187 memcpy(*to, enc.value, enc.length);
188 len = curlx_uztosi(enc.length);
189 gss_release_buffer(&min, &enc);
190 return len;
191}
192
193static int
194krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
195{
196 int ret = AUTH_OK;
197 char *p;
198 const char *host = conn->host.name;
199 ssize_t nread;
200 curl_socklen_t l = sizeof(conn->local_addr);
201 CURLcode result;
202 const char *service = data->set.str[STRING_SERVICE_NAME] ?
203 data->set.str[STRING_SERVICE_NAME] :
204 "ftp";
205 const char *srv_host = "host";
206 gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
207 OM_uint32 maj, min;
208 gss_name_t gssname;
209 gss_ctx_id_t *context = app_data;
210 struct gss_channel_bindings_struct chan;
211 size_t base64_sz = 0;
212 struct sockaddr_in *remote_addr =
213 (struct sockaddr_in *)(void *)&conn->remote_addr->sa_addr;
214 char *stringp;
215
216 if(getsockname(conn->sock[FIRSTSOCKET],
217 (struct sockaddr *)&conn->local_addr, &l) < 0)
218 perror("getsockname()");
219
220 chan.initiator_addrtype = GSS_C_AF_INET;
221 chan.initiator_address.length = l - 4;
222 chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
223 chan.acceptor_addrtype = GSS_C_AF_INET;
224 chan.acceptor_address.length = l - 4;
225 chan.acceptor_address.value = &remote_addr->sin_addr.s_addr;
226 chan.application_data.length = 0;
227 chan.application_data.value = NULL;
228
229 /* this loop will execute twice (once for service, once for host) */
230 for(;;) {
231 /* this really shouldn't be repeated here, but can't help it */
232 if(service == srv_host) {
233 result = ftpsend(data, conn, "AUTH GSSAPI");
234 if(result)
235 return -2;
236
237 if(Curl_GetFTPResponse(data, &nread, NULL))
238 return -1;
239
240 if(data->state.buffer[0] != '3')
241 return -1;
242 }
243
244 stringp = aprintf("%s@%s", service, host);
245 if(!stringp)
246 return -2;
247
248 input_buffer.value = stringp;
249 input_buffer.length = strlen(stringp);
250 maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE,
251 &gssname);
252 free(stringp);
253 if(maj != GSS_S_COMPLETE) {
254 gss_release_name(&min, &gssname);
255 if(service == srv_host) {
256 failf(data, "Error importing service name %s@%s", service, host);
257 return AUTH_ERROR;
258 }
259 service = srv_host;
260 continue;
261 }
262 /* We pass NULL as |output_name_type| to avoid a leak. */
263 gss_display_name(&min, gssname, &output_buffer, NULL);
264 infof(data, "Trying against %s", (char *)output_buffer.value);
265 gssresp = GSS_C_NO_BUFFER;
266 *context = GSS_C_NO_CONTEXT;
267
268 do {
269 /* Release the buffer at each iteration to avoid leaking: the first time
270 we are releasing the memory from gss_display_name. The last item is
271 taken care by a final gss_release_buffer. */
272 gss_release_buffer(&min, &output_buffer);
273 ret = AUTH_OK;
274 maj = Curl_gss_init_sec_context(data,
275 &min,
276 context,
277 gssname,
278 &Curl_krb5_mech_oid,
279 &chan,
280 gssresp,
281 &output_buffer,
282 TRUE,
283 NULL);
284
285 if(gssresp) {
286 free(_gssresp.value);
287 gssresp = NULL;
288 }
289
290 if(GSS_ERROR(maj)) {
291 infof(data, "Error creating security context");
292 ret = AUTH_ERROR;
293 break;
294 }
295
296 if(output_buffer.length) {
297 char *cmd;
298
299 result = Curl_base64_encode((char *)output_buffer.value,
300 output_buffer.length, &p, &base64_sz);
301 if(result) {
302 infof(data, "base64-encoding: %s", curl_easy_strerror(result));
303 ret = AUTH_ERROR;
304 break;
305 }
306
307 cmd = aprintf("ADAT %s", p);
308 if(cmd)
309 result = ftpsend(data, conn, cmd);
310 else
311 result = CURLE_OUT_OF_MEMORY;
312
313 free(p);
314 free(cmd);
315
316 if(result) {
317 ret = -2;
318 break;
319 }
320
321 if(Curl_GetFTPResponse(data, &nread, NULL)) {
322 ret = -1;
323 break;
324 }
325
326 if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
327 infof(data, "Server didn't accept auth data");
328 ret = AUTH_ERROR;
329 break;
330 }
331
332 _gssresp.value = NULL; /* make sure it is initialized */
333 p = data->state.buffer + 4;
334 p = strstr(p, "ADAT=");
335 if(p) {
336 result = Curl_base64_decode(p + 5,
337 (unsigned char **)&_gssresp.value,
338 &_gssresp.length);
339 if(result) {
340 failf(data, "base64-decoding: %s", curl_easy_strerror(result));
341 ret = AUTH_CONTINUE;
342 break;
343 }
344 }
345
346 gssresp = &_gssresp;
347 }
348 } while(maj == GSS_S_CONTINUE_NEEDED);
349
350 gss_release_name(&min, &gssname);
351 gss_release_buffer(&min, &output_buffer);
352
353 if(gssresp)
354 free(_gssresp.value);
355
356 if(ret == AUTH_OK || service == srv_host)
357 return ret;
358
359 service = srv_host;
360 }
361 return ret;
362}
363
364static void krb5_end(void *app_data)
365{
366 OM_uint32 min;
367 gss_ctx_id_t *context = app_data;
368 if(*context != GSS_C_NO_CONTEXT) {
369 OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
370 (void)maj;
371 DEBUGASSERT(maj == GSS_S_COMPLETE);
372 }
373}
374
375static const struct Curl_sec_client_mech Curl_krb5_client_mech = {
376 "GSSAPI",
377 sizeof(gss_ctx_id_t),
378 krb5_init,
379 krb5_auth,
380 krb5_end,
381 krb5_check_prot,
382
383 krb5_encode,
384 krb5_decode
385};
386
387static const struct {
388 unsigned char level;
389 const char *name;
390} level_names[] = {
391 { PROT_CLEAR, "clear" },
392 { PROT_SAFE, "safe" },
393 { PROT_CONFIDENTIAL, "confidential" },
394 { PROT_PRIVATE, "private" }
395};
396
397static unsigned char name_to_level(const char *name)
398{
399 int i;
400 for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
401 if(curl_strequal(name, level_names[i].name))
402 return level_names[i].level;
403 return PROT_NONE;
404}
405
406/* Convert a protocol |level| to its char representation.
407 We take an int to catch programming mistakes. */
408static char level_to_char(int level)
409{
410 switch(level) {
411 case PROT_CLEAR:
412 return 'C';
413 case PROT_SAFE:
414 return 'S';
415 case PROT_CONFIDENTIAL:
416 return 'E';
417 case PROT_PRIVATE:
418 return 'P';
419 case PROT_CMD:
420 /* Fall through */
421 default:
422 /* Those 2 cases should not be reached! */
423 break;
424 }
425 DEBUGASSERT(0);
426 /* Default to the most secure alternative. */
427 return 'P';
428}
429
430/* Send an FTP command defined by |message| and the optional arguments. The
431 function returns the ftp_code. If an error occurs, -1 is returned. */
432static int ftp_send_command(struct Curl_easy *data, const char *message, ...)
433{
434 int ftp_code;
435 ssize_t nread = 0;
436 va_list args;
437 char print_buffer[50];
438
439 va_start(args, message);
440 mvsnprintf(print_buffer, sizeof(print_buffer), message, args);
441 va_end(args);
442
443 if(ftpsend(data, data->conn, print_buffer)) {
444 ftp_code = -1;
445 }
446 else {
447 if(Curl_GetFTPResponse(data, &nread, &ftp_code))
448 ftp_code = -1;
449 }
450
451 (void)nread; /* Unused */
452 return ftp_code;
453}
454
455/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode
456 saying whether an error occurred or CURLE_OK if |len| was read. */
457static CURLcode
458socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len)
459{
460 char *to_p = to;
461 CURLcode result;
462 ssize_t nread = 0;
463
464 while(len > 0) {
465 nread = Curl_conn_recv(data, sockindex, to_p, len, &result);
466 if(nread > 0) {
467 len -= nread;
468 to_p += nread;
469 }
470 else {
471 if(result == CURLE_AGAIN)
472 continue;
473 return result;
474 }
475 }
476 return CURLE_OK;
477}
478
479
480/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a
481 CURLcode saying whether an error occurred or CURLE_OK if |len| was
482 written. */
483static CURLcode
484socket_write(struct Curl_easy *data, int sockindex, const void *to,
485 size_t len)
486{
487 const char *to_p = to;
488 CURLcode result;
489 ssize_t written;
490
491 while(len > 0) {
492 written = Curl_conn_send(data, sockindex, to_p, len, &result);
493 if(written > 0) {
494 len -= written;
495 to_p += written;
496 }
497 else {
498 if(result == CURLE_AGAIN)
499 continue;
500 return result;
501 }
502 }
503 return CURLE_OK;
504}
505
506static CURLcode read_data(struct Curl_easy *data, int sockindex,
507 struct krb5buffer *buf)
508{
509 struct connectdata *conn = data->conn;
510 int len;
511 CURLcode result;
512 int nread;
513
514 result = socket_read(data, sockindex, &len, sizeof(len));
515 if(result)
516 return result;
517
518 if(len) {
519 /* only realloc if there was a length */
520 len = ntohl(len);
521 if(len > CURL_MAX_INPUT_LENGTH)
522 len = 0;
523 else
524 buf->data = Curl_saferealloc(buf->data, len);
525 }
526 if(!len || !buf->data)
527 return CURLE_OUT_OF_MEMORY;
528
529 result = socket_read(data, sockindex, buf->data, len);
530 if(result)
531 return result;
532 nread = conn->mech->decode(conn->app_data, buf->data, len,
533 conn->data_prot, conn);
534 if(nread < 0)
535 return CURLE_RECV_ERROR;
536 buf->size = (size_t)nread;
537 buf->index = 0;
538 return CURLE_OK;
539}
540
541static size_t
542buffer_read(struct krb5buffer *buf, void *data, size_t len)
543{
544 if(buf->size - buf->index < len)
545 len = buf->size - buf->index;
546 memcpy(data, (char *)buf->data + buf->index, len);
547 buf->index += len;
548 return len;
549}
550
551/* Matches Curl_recv signature */
552static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
553 char *buffer, size_t len, CURLcode *err)
554{
555 size_t bytes_read;
556 size_t total_read = 0;
557 struct connectdata *conn = data->conn;
558
559 *err = CURLE_OK;
560
561 /* Handle clear text response. */
562 if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
563 return Curl_conn_recv(data, sockindex, buffer, len, err);
564
565 if(conn->in_buffer.eof_flag) {
566 conn->in_buffer.eof_flag = 0;
567 return 0;
568 }
569
570 bytes_read = buffer_read(&conn->in_buffer, buffer, len);
571 len -= bytes_read;
572 total_read += bytes_read;
573 buffer += bytes_read;
574
575 while(len > 0) {
576 if(read_data(data, sockindex, &conn->in_buffer))
577 return -1;
578 if(conn->in_buffer.size == 0) {
579 if(bytes_read > 0)
580 conn->in_buffer.eof_flag = 1;
581 return bytes_read;
582 }
583 bytes_read = buffer_read(&conn->in_buffer, buffer, len);
584 len -= bytes_read;
585 total_read += bytes_read;
586 buffer += bytes_read;
587 }
588 return total_read;
589}
590
591/* Send |length| bytes from |from| to the |fd| socket taking care of encoding
592 and negotiating with the server. |from| can be NULL. */
593static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
594 curl_socket_t fd, const char *from, int length)
595{
596 int bytes, htonl_bytes; /* 32-bit integers for htonl */
597 char *buffer = NULL;
598 char *cmd_buffer;
599 size_t cmd_size = 0;
600 CURLcode error;
601 enum protection_level prot_level = conn->data_prot;
602 bool iscmd = (prot_level == PROT_CMD)?TRUE:FALSE;
603
604 DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST);
605
606 if(iscmd) {
607 if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5))
608 prot_level = PROT_PRIVATE;
609 else
610 prot_level = conn->command_prot;
611 }
612 bytes = conn->mech->encode(conn->app_data, from, length, prot_level,
613 (void **)&buffer);
614 if(!buffer || bytes <= 0)
615 return; /* error */
616
617 if(iscmd) {
618 error = Curl_base64_encode(buffer, curlx_sitouz(bytes),
619 &cmd_buffer, &cmd_size);
620 if(error) {
621 free(buffer);
622 return; /* error */
623 }
624 if(cmd_size > 0) {
625 static const char *enc = "ENC ";
626 static const char *mic = "MIC ";
627 if(prot_level == PROT_PRIVATE)
628 socket_write(data, fd, enc, 4);
629 else
630 socket_write(data, fd, mic, 4);
631
632 socket_write(data, fd, cmd_buffer, cmd_size);
633 socket_write(data, fd, "\r\n", 2);
634 infof(data, "Send: %s%s", prot_level == PROT_PRIVATE?enc:mic,
635 cmd_buffer);
636 free(cmd_buffer);
637 }
638 }
639 else {
640 htonl_bytes = htonl(bytes);
641 socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes));
642 socket_write(data, fd, buffer, curlx_sitouz(bytes));
643 }
644 free(buffer);
645}
646
647static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn,
648 curl_socket_t fd, const char *buffer, size_t length)
649{
650 ssize_t tx = 0, len = conn->buffer_size;
651
652 if(len <= 0)
653 len = length;
654 while(length) {
655 if(length < (size_t)len)
656 len = length;
657
658 do_sec_send(data, conn, fd, buffer, curlx_sztosi(len));
659 length -= len;
660 buffer += len;
661 tx += len;
662 }
663 return tx;
664}
665
666/* Matches Curl_send signature */
667static ssize_t sec_send(struct Curl_easy *data, int sockindex,
668 const void *buffer, size_t len, CURLcode *err)
669{
670 struct connectdata *conn = data->conn;
671 curl_socket_t fd = conn->sock[sockindex];
672 *err = CURLE_OK;
673 return sec_write(data, conn, fd, buffer, len);
674}
675
676int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,
677 char *buffer, enum protection_level level)
678{
679 /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an
680 int */
681 int decoded_len;
682 char *buf;
683 int ret_code = 0;
684 size_t decoded_sz = 0;
685 CURLcode error;
686
687 (void) data;
688
689 if(!conn->mech)
690 /* not initialized, return error */
691 return -1;
692
693 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
694
695 error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
696 if(error || decoded_sz == 0)
697 return -1;
698
699 if(decoded_sz > (size_t)INT_MAX) {
700 free(buf);
701 return -1;
702 }
703 decoded_len = curlx_uztosi(decoded_sz);
704
705 decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len,
706 level, conn);
707 if(decoded_len <= 0) {
708 free(buf);
709 return -1;
710 }
711
712 {
713 buf[decoded_len] = '\n';
714 Curl_debug(data, CURLINFO_HEADER_IN, buf, decoded_len + 1);
715 }
716
717 buf[decoded_len] = '\0';
718 if(decoded_len <= 3)
719 /* suspiciously short */
720 return 0;
721
722 if(buf[3] != '-')
723 ret_code = atoi(buf);
724
725 if(buf[decoded_len - 1] == '\n')
726 buf[decoded_len - 1] = '\0';
727 strcpy(buffer, buf);
728 free(buf);
729 return ret_code;
730}
731
732static int sec_set_protection_level(struct Curl_easy *data)
733{
734 int code;
735 struct connectdata *conn = data->conn;
736 unsigned char level = conn->request_data_prot;
737
738 DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
739
740 if(!conn->sec_complete) {
741 infof(data, "Trying to change the protection level after the"
742 " completion of the data exchange.");
743 return -1;
744 }
745
746 /* Bail out if we try to set up the same level */
747 if(conn->data_prot == level)
748 return 0;
749
750 if(level) {
751 char *pbsz;
752 unsigned int buffer_size = 1 << 20; /* 1048576 */
753
754 code = ftp_send_command(data, "PBSZ %u", buffer_size);
755 if(code < 0)
756 return -1;
757
758 if(code/100 != 2) {
759 failf(data, "Failed to set the protection's buffer size.");
760 return -1;
761 }
762 conn->buffer_size = buffer_size;
763
764 pbsz = strstr(data->state.buffer, "PBSZ=");
765 if(pbsz) {
766 /* stick to default value if the check fails */
767 if(!strncmp(pbsz, "PBSZ=", 5) && ISDIGIT(pbsz[5]))
768 buffer_size = atoi(&pbsz[5]);
769 if(buffer_size < conn->buffer_size)
770 conn->buffer_size = buffer_size;
771 }
772 }
773
774 /* Now try to negotiate the protection level. */
775 code = ftp_send_command(data, "PROT %c", level_to_char(level));
776
777 if(code < 0)
778 return -1;
779
780 if(code/100 != 2) {
781 failf(data, "Failed to set the protection level.");
782 return -1;
783 }
784
785 conn->data_prot = level;
786 if(level == PROT_PRIVATE)
787 conn->command_prot = level;
788
789 return 0;
790}
791
792int
793Curl_sec_request_prot(struct connectdata *conn, const char *level)
794{
795 unsigned char l = name_to_level(level);
796 if(l == PROT_NONE)
797 return -1;
798 DEBUGASSERT(l > PROT_NONE && l < PROT_LAST);
799 conn->request_data_prot = l;
800 return 0;
801}
802
803static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
804{
805 int ret;
806 void *tmp_allocation;
807 const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech;
808
809 tmp_allocation = realloc(conn->app_data, mech->size);
810 if(!tmp_allocation) {
811 failf(data, "Failed realloc of size %zu", mech->size);
812 mech = NULL;
813 return CURLE_OUT_OF_MEMORY;
814 }
815 conn->app_data = tmp_allocation;
816
817 if(mech->init) {
818 ret = mech->init(conn->app_data);
819 if(ret) {
820 infof(data, "Failed initialization for %s. Skipping it.",
821 mech->name);
822 return CURLE_FAILED_INIT;
823 }
824 }
825
826 infof(data, "Trying mechanism %s...", mech->name);
827 ret = ftp_send_command(data, "AUTH %s", mech->name);
828 if(ret < 0)
829 return CURLE_COULDNT_CONNECT;
830
831 if(ret/100 != 3) {
832 switch(ret) {
833 case 504:
834 infof(data, "Mechanism %s is not supported by the server (server "
835 "returned ftp code: 504).", mech->name);
836 break;
837 case 534:
838 infof(data, "Mechanism %s was rejected by the server (server returned "
839 "ftp code: 534).", mech->name);
840 break;
841 default:
842 if(ret/100 == 5) {
843 infof(data, "server does not support the security extensions");
844 return CURLE_USE_SSL_FAILED;
845 }
846 break;
847 }
848 return CURLE_LOGIN_DENIED;
849 }
850
851 /* Authenticate */
852 ret = mech->auth(conn->app_data, data, conn);
853
854 if(ret != AUTH_CONTINUE) {
855 if(ret != AUTH_OK) {
856 /* Mechanism has dumped the error to stderr, don't error here. */
857 return CURLE_USE_SSL_FAILED;
858 }
859 DEBUGASSERT(ret == AUTH_OK);
860
861 conn->mech = mech;
862 conn->sec_complete = 1;
863 conn->recv[FIRSTSOCKET] = sec_recv;
864 conn->send[FIRSTSOCKET] = sec_send;
865 conn->recv[SECONDARYSOCKET] = sec_recv;
866 conn->send[SECONDARYSOCKET] = sec_send;
867 conn->command_prot = PROT_SAFE;
868 /* Set the requested protection level */
869 /* BLOCKING */
870 (void)sec_set_protection_level(data);
871 }
872
873 return CURLE_OK;
874}
875
876CURLcode
877Curl_sec_login(struct Curl_easy *data, struct connectdata *conn)
878{
879 return choose_mech(data, conn);
880}
881
882
883void
884Curl_sec_end(struct connectdata *conn)
885{
886 if(conn->mech && conn->mech->end)
887 conn->mech->end(conn->app_data);
888 free(conn->app_data);
889 conn->app_data = NULL;
890 if(conn->in_buffer.data) {
891 free(conn->in_buffer.data);
892 conn->in_buffer.data = NULL;
893 conn->in_buffer.size = 0;
894 conn->in_buffer.index = 0;
895 conn->in_buffer.eof_flag = 0;
896 }
897 conn->sec_complete = 0;
898 conn->data_prot = PROT_CLEAR;
899 conn->mech = NULL;
900}
901
902#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */
903