]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/curl/lib/security.c
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / curl / lib / security.c
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.
3  *
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
8  * below.
9  *
10  * Copyright (c) 1998, 1999 Kungliga Tekniska Högskolan
11  * (Royal Institute of Technology, Stockholm, Sweden).
12  * All rights reserved.
13  * 
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  * 
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 
25  * 3. Neither the name of the Institute nor the names of its contributors
26  *    may be used to endorse or promote products derived from this software
27  *    without specific prior written permission.
28  * 
29  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39  * SUCH DAMAGE.  */
40
41 #include "setup.h"
42
43 #ifndef CURL_DISABLE_FTP
44 #ifdef HAVE_KRB4
45
46 #define _MPRINTF_REPLACE /* we want curl-functions instead of native ones */
47 #include <curl/mprintf.h>
48
49 #include "security.h"
50 #include <stdlib.h>
51 #include <string.h>
52 #include <netdb.h>
53
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif
57
58 #include "base64.h"
59 #include "sendf.h"
60 #include "ftp.h"
61
62 /* The last #include file should be: */
63 #ifdef CURLDEBUG
64 #include "memdebug.h"
65 #endif
66
67 #define min(a, b)   ((a) < (b) ? (a) : (b))
68
69 static struct {
70     enum protection_level level;
71     const char *name;
72 } level_names[] = {
73     { prot_clear, "clear" },
74     { prot_safe, "safe" },
75     { prot_confidential, "confidential" },
76     { prot_private, "private" }
77 };
78
79 static enum protection_level 
80 name_to_level(const char *name)
81 {
82   int i;
83   for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
84     if(!strncasecmp(level_names[i].name, name, strlen(name)))
85       return level_names[i].level;
86   return (enum protection_level)-1;
87 }
88
89 static struct Curl_sec_client_mech *mechs[] = {
90 #ifdef KRB5
91   /* not supported */
92 #endif
93 #ifdef HAVE_KRB4
94     &Curl_krb4_client_mech,
95 #endif
96     NULL
97 };
98
99 int
100 Curl_sec_getc(struct connectdata *conn, FILE *F)
101 {
102   if(conn->sec_complete && conn->data_prot) {
103     char c;
104     if(Curl_sec_read(conn, fileno(F), &c, 1) <= 0)
105       return EOF;
106     return c;
107   }
108   else
109     return getc(F);
110 }
111
112 static int
113 block_read(int fd, void *buf, size_t len)
114 {
115   unsigned char *p = buf;
116   int b;
117   while(len) {
118     b = read(fd, p, len);
119     if (b == 0)
120       return 0;
121     else if (b < 0)
122       return -1;
123     len -= b;
124     p += b;
125   }
126   return p - (unsigned char*)buf;
127 }
128
129 static int
130 block_write(int fd, void *buf, size_t len)
131 {
132   unsigned char *p = buf;
133   int b;
134   while(len) {
135     b = write(fd, p, len);
136     if(b < 0)
137       return -1;
138     len -= b;
139     p += b;
140   }
141   return p - (unsigned char*)buf;
142 }
143
144 static int
145 sec_get_data(struct connectdata *conn,
146              int fd, struct krb4buffer *buf)
147 {
148   int len;
149   int b;
150   
151   b = block_read(fd, &len, sizeof(len));
152   if (b == 0)
153     return 0;
154   else if (b < 0)
155     return -1;
156   len = ntohl(len);
157   buf->data = realloc(buf->data, len);
158   b = block_read(fd, buf->data, len);
159   if (b == 0)
160     return 0;
161   else if (b < 0)
162     return -1;
163   buf->size = (conn->mech->decode)(conn->app_data, buf->data, len,
164                                    conn->data_prot, conn);
165   buf->index = 0;
166   return 0;
167 }
168
169 static size_t
170 buffer_read(struct krb4buffer *buf, void *data, size_t len)
171 {
172     len = min(len, buf->size - buf->index);
173     memcpy(data, (char*)buf->data + buf->index, len);
174     buf->index += len;
175     return len;
176 }
177
178 static size_t
179 buffer_write(struct krb4buffer *buf, void *data, size_t len)
180 {
181     if(buf->index + len > buf->size) {
182         void *tmp;
183         if(buf->data == NULL)
184             tmp = malloc(1024);
185         else
186             tmp = realloc(buf->data, buf->index + len);
187         if(tmp == NULL)
188             return -1;
189         buf->data = tmp;
190         buf->size = buf->index + len;
191     }
192     memcpy((char*)buf->data + buf->index, data, len);
193     buf->index += len;
194     return len;
195 }
196
197 int
198 Curl_sec_read(struct connectdata *conn, int fd, void *buffer, int length)
199 {
200     size_t len;
201     int rx = 0;
202
203     if(conn->sec_complete == 0 || conn->data_prot == 0)
204       return read(fd, buffer, length);
205
206     if(conn->in_buffer.eof_flag){
207       conn->in_buffer.eof_flag = 0;
208       return 0;
209     }
210     
211     len = buffer_read(&conn->in_buffer, buffer, length);
212     length -= len;
213     rx += len;
214     buffer = (char*)buffer + len;
215     
216     while(length) {
217       if(sec_get_data(conn, fd, &conn->in_buffer) < 0)
218         return -1;
219       if(conn->in_buffer.size == 0) {
220         if(rx)
221           conn->in_buffer.eof_flag = 1;
222         return rx;
223       }
224       len = buffer_read(&conn->in_buffer, buffer, length);
225       length -= len;
226       rx += len;
227       buffer = (char*)buffer + len;
228     }
229     return rx;
230 }
231
232 static int
233 sec_send(struct connectdata *conn, int fd, char *from, int length)
234 {
235   int bytes;
236   void *buf;
237   bytes = (conn->mech->encode)(conn->app_data, from, length, conn->data_prot,
238                                &buf, conn);
239   bytes = htonl(bytes);
240   block_write(fd, &bytes, sizeof(bytes));
241   block_write(fd, buf, ntohl(bytes));
242   free(buf);
243   return length;
244 }
245
246 int
247 Curl_sec_fflush_fd(struct connectdata *conn, int fd)
248 {
249   if(conn->data_prot != prot_clear) {
250     if(conn->out_buffer.index > 0){
251       Curl_sec_write(conn, fd,
252                 conn->out_buffer.data, conn->out_buffer.index);
253       conn->out_buffer.index = 0;
254     }
255     sec_send(conn, fd, NULL, 0);
256   }
257   return 0;
258 }
259
260 int
261 Curl_sec_write(struct connectdata *conn, int fd, char *buffer, int length)
262 {
263   int len = conn->buffer_size;
264   int tx = 0;
265       
266   if(conn->data_prot == prot_clear)
267     return write(fd, buffer, length);
268
269   len -= (conn->mech->overhead)(conn->app_data, conn->data_prot, len);
270   while(length){
271     if(length < len)
272       len = length;
273     sec_send(conn, fd, buffer, len);
274     length -= len;
275     buffer += len;
276     tx += len;
277   }
278   return tx;
279 }
280
281 int
282 Curl_sec_putc(struct connectdata *conn, int c, FILE *F)
283 {
284   char ch = c;
285   if(conn->data_prot == prot_clear)
286     return putc(c, F);
287     
288   buffer_write(&conn->out_buffer, &ch, 1);
289   if(c == '\n' || conn->out_buffer.index >= 1024 /* XXX */) {
290     Curl_sec_write(conn, fileno(F), conn->out_buffer.data,
291                    conn->out_buffer.index);
292     conn->out_buffer.index = 0;
293   }
294   return c;
295 }
296
297 int
298 Curl_sec_read_msg(struct connectdata *conn, char *s, int level)
299 {
300     int len;
301     char *buf;
302     int code;
303     
304     buf = malloc(strlen(s));
305     len = Curl_base64_decode(s + 4, buf); /* XXX */
306     
307     len = (conn->mech->decode)(conn->app_data, buf, len, level, conn);
308     if(len < 0)
309         return -1;
310     
311     buf[len] = '\0';
312
313     if(buf[3] == '-')
314         code = 0;
315     else
316         sscanf(buf, "%d", &code);
317     if(buf[len-1] == '\n')
318         buf[len-1] = '\0';
319     strcpy(s, buf);
320     free(buf);
321     return code;
322 }
323
324 enum protection_level
325 Curl_set_command_prot(struct connectdata *conn, enum protection_level level)
326 {
327     enum protection_level old = conn->command_prot;
328     conn->command_prot = level;
329     return old;
330 }
331
332 static int
333 sec_prot_internal(struct connectdata *conn, int level)
334 {
335   char *p;
336   unsigned int s = 1048576;
337   ssize_t nread;
338
339   if(!conn->sec_complete){
340     infof(conn->data, "No security data exchange has taken place.\n");
341     return -1;
342   }
343
344   if(level){
345     int code;
346     if(Curl_ftpsendf(conn, "PBSZ %u", s))
347       return -1;
348
349     if(Curl_GetFTPResponse(&nread, conn, &code))
350       return -1;
351
352     if(code/100 != '2'){
353       failf(conn->data, "Failed to set protection buffer size.");
354       return -1;
355     }
356     conn->buffer_size = s;
357
358     p = strstr(conn->data->state.buffer, "PBSZ=");
359     if(p)
360       sscanf(p, "PBSZ=%u", &s);
361     if(s < conn->buffer_size)
362       conn->buffer_size = s;
363   }
364
365   if(Curl_ftpsendf(conn, "PROT %c", level["CSEP"]))
366     return -1;
367
368   if(Curl_GetFTPResponse(&nread, conn, NULL))
369     return -1;
370
371   if(conn->data->state.buffer[0] != '2'){
372     failf(conn->data, "Failed to set protection level.");
373     return -1;
374   }
375     
376   conn->data_prot = (enum protection_level)level;
377   return 0;
378 }
379
380 void
381 Curl_sec_set_protection_level(struct connectdata *conn)
382 {
383   if(conn->sec_complete && conn->data_prot != conn->request_data_prot)
384     sec_prot_internal(conn, conn->request_data_prot);
385 }
386
387
388 int
389 Curl_sec_request_prot(struct connectdata *conn, const char *level)
390 {
391   int l = name_to_level(level);
392   if(l == -1)
393     return -1;
394   conn->request_data_prot = (enum protection_level)l;
395   return 0;
396 }
397
398 int
399 Curl_sec_login(struct connectdata *conn)
400 {
401   int ret;
402   struct Curl_sec_client_mech **m;
403   ssize_t nread;
404   struct SessionHandle *data=conn->data;
405   int ftpcode;
406
407   for(m = mechs; *m && (*m)->name; m++) {
408     void *tmp;
409
410     tmp = realloc(conn->app_data, (*m)->size);
411     if (tmp == NULL) {
412       failf (data, "realloc %u failed", (*m)->size);
413       return -1;
414     }
415     conn->app_data = tmp;
416             
417     if((*m)->init && (*(*m)->init)(conn->app_data) != 0) {
418       infof(data, "Skipping %s...\n", (*m)->name);
419       continue;
420     }
421     infof(data, "Trying %s...\n", (*m)->name);
422
423     if(Curl_ftpsendf(conn, "AUTH %s", (*m)->name))
424       return -1;
425
426     if(Curl_GetFTPResponse(&nread, conn, &ftpcode))
427       return -1;
428
429     if(conn->data->state.buffer[0] != '3'){
430       switch(ftpcode) {
431       case 504:
432         infof(data,
433               "%s is not supported by the server.\n", (*m)->name);
434         break;
435       case 534:
436         infof(data, "%s rejected as security mechanism.\n", (*m)->name);
437         break;
438       default:
439         if(conn->data->state.buffer[0] == '5') {
440           infof(data, "The server doesn't support the FTP "
441                 "security extensions.\n");
442           return -1;
443         }
444         break;
445       }
446       continue;
447     }
448
449     ret = (*(*m)->auth)(conn->app_data, conn);
450         
451     if(ret == AUTH_CONTINUE)
452       continue;
453     else if(ret != AUTH_OK){
454       /* mechanism is supposed to output error string */
455       return -1;
456     }
457     conn->mech = *m;
458     conn->sec_complete = 1;
459     conn->command_prot = prot_safe;
460     break;
461   }
462     
463   return *m == NULL;
464 }
465
466 void
467 Curl_sec_end(struct connectdata *conn)
468 {
469   if (conn->mech != NULL) {
470     if(conn->mech->end)
471       (conn->mech->end)(conn->app_data);
472     memset(conn->app_data, 0, conn->mech->size);
473     free(conn->app_data);
474     conn->app_data = NULL;
475   }
476   conn->sec_complete = 0;
477   conn->data_prot = (enum protection_level)0;
478   conn->mech=NULL;
479 }
480
481 #endif /* HAVE_KRB4 */
482 #endif /* CURL_DISABLE_FTP */