1 /* This source code was modified by Martin Hedenfalk <mhe@stacken.kth.se> for
2 * use in Curl. His latest changes were done 2000-09-18.
4 * It has since been patched and modified a lot by Daniel Stenberg
5 * <daniel@haxx.se> to make it better applied to curl conditions, and to make
6 * it not use globals, pollute name space and more. This source code awaits a
7 * rewrite to work around the paragraph 2 in the BSD licenses as explained
10 * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
11 * (Royal Institute of Technology, Stockholm, Sweden).
13 * Copyright (C) 2001 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
15 * All rights reserved.
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
24 * 2. Redistributions in binary form must reproduce the above copyright
25 * notice, this list of conditions and the following disclaimer in the
26 * documentation and/or other materials provided with the distribution.
28 * 3. Neither the name of the Institute nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
32 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46 #ifndef CURL_DISABLE_FTP
47 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
49 #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
50 #include <curl/mprintf.h>
65 #include "curl_base64.h"
70 /* The last #include file should be: */
74 enum protection_level level
;
77 { prot_clear
, "clear" },
78 { prot_safe
, "safe" },
79 { prot_confidential
, "confidential" },
80 { prot_private
, "private" }
83 static enum protection_level
84 name_to_level(const char *name
)
87 for(i
= 0; i
< (int)sizeof(level_names
)/(int)sizeof(level_names
[0]); i
++)
88 if(curl_strnequal(level_names
[i
].name
, name
, strlen(name
)))
89 return level_names
[i
].level
;
90 return (enum protection_level
)-1;
93 static const struct Curl_sec_client_mech
* const mechs
[] = {
95 &Curl_krb5_client_mech
,
98 &Curl_krb4_client_mech
,
104 Curl_sec_getc(struct connectdata
*conn
, FILE *F
)
106 if(conn
->sec_complete
&& conn
->data_prot
) {
108 if(Curl_sec_read(conn
, fileno(F
), &c
, 1) <= 0)
117 block_read(int fd
, void *buf
, size_t len
)
119 unsigned char *p
= buf
;
122 b
= read(fd
, p
, len
);
125 else if(b
< 0 && (errno
== EINTR
|| errno
== EAGAIN
))
132 return p
- (unsigned char*)buf
;
136 block_write(int fd
, const void *buf
, size_t len
)
138 const unsigned char *p
= buf
;
141 b
= write(fd
, p
, len
);
142 if(b
< 0 && (errno
== EINTR
|| errno
== EAGAIN
))
149 return p
- (unsigned char*)buf
;
153 sec_get_data(struct connectdata
*conn
,
154 int fd
, struct krb4buffer
*buf
)
159 b
= block_read(fd
, &len
, sizeof(len
));
165 buf
->data
= realloc(buf
->data
, len
);
166 b
= buf
->data
? block_read(fd
, buf
->data
, len
) : -1;
171 buf
->size
= (conn
->mech
->decode
)(conn
->app_data
, buf
->data
, len
,
172 conn
->data_prot
, conn
);
178 buffer_read(struct krb4buffer
*buf
, void *data
, size_t len
)
180 if(buf
->size
- buf
->index
< len
)
181 len
= buf
->size
- buf
->index
;
182 memcpy(data
, (char*)buf
->data
+ buf
->index
, len
);
188 buffer_write(struct krb4buffer
*buf
, void *data
, size_t len
)
190 if(buf
->index
+ len
> buf
->size
) {
192 if(buf
->data
== NULL
)
195 tmp
= realloc(buf
->data
, buf
->index
+ len
);
199 buf
->size
= buf
->index
+ len
;
201 memcpy((char*)buf
->data
+ buf
->index
, data
, len
);
207 Curl_sec_read(struct connectdata
*conn
, int fd
, void *buffer
, int length
)
212 if(conn
->sec_complete
== 0 || conn
->data_prot
== 0)
213 return read(fd
, buffer
, length
);
215 if(conn
->in_buffer
.eof_flag
){
216 conn
->in_buffer
.eof_flag
= 0;
220 len
= buffer_read(&conn
->in_buffer
, buffer
, length
);
223 buffer
= (char*)buffer
+ len
;
226 if(sec_get_data(conn
, fd
, &conn
->in_buffer
) < 0)
228 if(conn
->in_buffer
.size
== 0) {
230 conn
->in_buffer
.eof_flag
= 1;
233 len
= buffer_read(&conn
->in_buffer
, buffer
, length
);
236 buffer
= (char*)buffer
+ len
;
242 sec_send(struct connectdata
*conn
, int fd
, const char *from
, int length
)
246 enum protection_level protlevel
= conn
->data_prot
;
247 int iscmd
= protlevel
== prot_cmd
;
250 if(!strncmp(from
, "PASS ", 5) || !strncmp(from
, "ACCT ", 5))
251 protlevel
= prot_private
;
253 protlevel
= conn
->command_prot
;
255 bytes
= (conn
->mech
->encode
)(conn
->app_data
, from
, length
, protlevel
,
260 bytes
= Curl_base64_encode(conn
->data
, (char *)buf
, bytes
, &cmdbuf
);
262 if(protlevel
== prot_private
)
263 block_write(fd
, "ENC ", 4);
265 block_write(fd
, "MIC ", 4);
266 block_write(fd
, cmdbuf
, bytes
);
267 block_write(fd
, "\r\n", 2);
268 Curl_infof(conn
->data
, "%s %s\n",
269 protlevel
== prot_private
? "ENC" : "MIC", cmdbuf
);
274 bytes
= htonl(bytes
);
275 block_write(fd
, &bytes
, sizeof(bytes
));
276 block_write(fd
, buf
, ntohl(bytes
));
283 Curl_sec_fflush_fd(struct connectdata
*conn
, int fd
)
285 if(conn
->data_prot
!= prot_clear
) {
286 if(conn
->out_buffer
.index
> 0){
287 Curl_sec_write(conn
, fd
,
288 conn
->out_buffer
.data
, conn
->out_buffer
.index
);
289 conn
->out_buffer
.index
= 0;
291 sec_send(conn
, fd
, NULL
, 0);
297 Curl_sec_write(struct connectdata
*conn
, int fd
, const char *buffer
, int length
)
299 int len
= conn
->buffer_size
;
302 if(conn
->data_prot
== prot_clear
)
303 return write(fd
, buffer
, length
);
305 len
-= (conn
->mech
->overhead
)(conn
->app_data
, conn
->data_prot
, len
);
311 sec_send(conn
, fd
, buffer
, len
);
320 Curl_sec_send(struct connectdata
*conn
, int num
, const char *buffer
, int length
)
322 curl_socket_t fd
= conn
->sock
[num
];
323 return (ssize_t
)Curl_sec_write(conn
, fd
, buffer
, length
);
327 Curl_sec_putc(struct connectdata
*conn
, int c
, FILE *F
)
330 if(conn
->data_prot
== prot_clear
)
333 buffer_write(&conn
->out_buffer
, &ch
, 1);
334 if(c
== '\n' || conn
->out_buffer
.index
>= 1024 /* XXX */) {
335 Curl_sec_write(conn
, fileno(F
), conn
->out_buffer
.data
,
336 conn
->out_buffer
.index
);
337 conn
->out_buffer
.index
= 0;
343 Curl_sec_read_msg(struct connectdata
*conn
, char *s
, int level
)
349 len
= Curl_base64_decode(s
+ 4, &buf
); /* XXX */
351 len
= (conn
->mech
->decode
)(conn
->app_data
, buf
, len
, level
, conn
);
360 if(conn
->data
->set
.verbose
) {
362 Curl_debug(conn
->data
, CURLINFO_HEADER_IN
, (char *)buf
, len
+ 1, conn
);
370 sscanf((char *)buf
, "%d", &code
);
371 if(buf
[len
-1] == '\n')
373 strcpy(s
, (char *)buf
);
378 enum protection_level
379 Curl_set_command_prot(struct connectdata
*conn
, enum protection_level level
)
381 enum protection_level old
= conn
->command_prot
;
382 conn
->command_prot
= level
;
387 sec_prot_internal(struct connectdata
*conn
, int level
)
390 unsigned int s
= 1048576;
393 if(!conn
->sec_complete
){
394 infof(conn
->data
, "No security data exchange has taken place.\n");
400 if(Curl_ftpsendf(conn
, "PBSZ %u", s
))
403 if(Curl_GetFTPResponse(&nread
, conn
, &code
))
407 failf(conn
->data
, "Failed to set protection buffer size.");
410 conn
->buffer_size
= s
;
412 p
= strstr(conn
->data
->state
.buffer
, "PBSZ=");
414 sscanf(p
, "PBSZ=%u", &s
);
415 if(s
< conn
->buffer_size
)
416 conn
->buffer_size
= s
;
419 if(Curl_ftpsendf(conn
, "PROT %c", level
["CSEP"]))
422 if(Curl_GetFTPResponse(&nread
, conn
, NULL
))
425 if(conn
->data
->state
.buffer
[0] != '2'){
426 failf(conn
->data
, "Failed to set protection level.");
430 conn
->data_prot
= (enum protection_level
)level
;
431 if(level
== prot_private
)
432 conn
->command_prot
= (enum protection_level
)level
;
437 Curl_sec_set_protection_level(struct connectdata
*conn
)
439 if(conn
->sec_complete
&& conn
->data_prot
!= conn
->request_data_prot
)
440 sec_prot_internal(conn
, conn
->request_data_prot
);
445 Curl_sec_request_prot(struct connectdata
*conn
, const char *level
)
447 int l
= name_to_level(level
);
450 conn
->request_data_prot
= (enum protection_level
)l
;
455 Curl_sec_login(struct connectdata
*conn
)
458 const struct Curl_sec_client_mech
* const *m
;
460 struct SessionHandle
*data
=conn
->data
;
463 for(m
= mechs
; *m
&& (*m
)->name
; m
++) {
466 tmp
= realloc(conn
->app_data
, (*m
)->size
);
468 failf (data
, "realloc %u failed", (*m
)->size
);
471 conn
->app_data
= tmp
;
473 if((*m
)->init
&& (*(*m
)->init
)(conn
->app_data
) != 0) {
474 infof(data
, "Skipping %s...\n", (*m
)->name
);
477 infof(data
, "Trying %s...\n", (*m
)->name
);
479 if(Curl_ftpsendf(conn
, "AUTH %s", (*m
)->name
))
482 if(Curl_GetFTPResponse(&nread
, conn
, &ftpcode
))
485 if(conn
->data
->state
.buffer
[0] != '3'){
489 "%s is not supported by the server.\n", (*m
)->name
);
492 infof(data
, "%s rejected as security mechanism.\n", (*m
)->name
);
495 if(conn
->data
->state
.buffer
[0] == '5') {
496 infof(data
, "The server doesn't support the FTP "
497 "security extensions.\n");
505 ret
= (*(*m
)->auth
)(conn
->app_data
, conn
);
507 if(ret
== AUTH_CONTINUE
)
509 else if(ret
!= AUTH_OK
){
510 /* mechanism is supposed to output error string */
514 conn
->sec_complete
= 1;
515 conn
->command_prot
= prot_safe
;
516 /* Set the requested protection level */
518 Curl_sec_set_protection_level(conn
);
526 Curl_sec_end(struct connectdata
*conn
)
528 if(conn
->mech
!= NULL
) {
530 (conn
->mech
->end
)(conn
->app_data
);
531 memset(conn
->app_data
, 0, conn
->mech
->size
);
532 free(conn
->app_data
);
533 conn
->app_data
= NULL
;
535 conn
->sec_complete
= 0;
536 conn
->data_prot
= (enum protection_level
)0;
540 #endif /* HAVE_KRB4 */
541 #endif /* CURL_DISABLE_FTP */