Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/heimdal/appl/ftp/ftpd/security.c
34907 views
1
/*
2
* Copyright (c) 1998-2002, 2005 Kungliga Tekniska Högskolan
3
* (Royal Institute of Technology, Stockholm, Sweden).
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
*
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
*
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* 3. Neither the name of the Institute nor the names of its contributors
18
* may be used to endorse or promote products derived from this software
19
* without specific prior written permission.
20
*
21
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31
* SUCH DAMAGE.
32
*/
33
34
#ifdef FTP_SERVER
35
#include "ftpd_locl.h"
36
#else
37
#include "ftp_locl.h"
38
#endif
39
40
RCSID("$Id$");
41
42
static enum protection_level command_prot;
43
static enum protection_level data_prot;
44
static size_t buffer_size;
45
46
struct buffer {
47
void *data;
48
size_t size;
49
size_t index;
50
int eof_flag;
51
};
52
53
static struct buffer in_buffer, out_buffer;
54
int sec_complete;
55
56
static struct {
57
enum protection_level level;
58
const char *name;
59
} level_names[] = {
60
{ prot_clear, "clear" },
61
{ prot_safe, "safe" },
62
{ prot_confidential, "confidential" },
63
{ prot_private, "private" }
64
};
65
66
static const char *
67
level_to_name(enum protection_level level)
68
{
69
int i;
70
for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
71
if(level_names[i].level == level)
72
return level_names[i].name;
73
return "unknown";
74
}
75
76
#ifndef FTP_SERVER /* not used in server */
77
static enum protection_level
78
name_to_level(const char *name)
79
{
80
int i;
81
for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
82
if(!strncasecmp(level_names[i].name, name, strlen(name)))
83
return level_names[i].level;
84
return prot_invalid;
85
}
86
#endif
87
88
#ifdef FTP_SERVER
89
90
static struct sec_server_mech *mechs[] = {
91
#ifdef KRB5
92
&gss_server_mech,
93
#endif
94
NULL
95
};
96
97
static struct sec_server_mech *mech;
98
99
#else
100
101
static struct sec_client_mech *mechs[] = {
102
#ifdef KRB5
103
&gss_client_mech,
104
#endif
105
NULL
106
};
107
108
static struct sec_client_mech *mech;
109
110
#endif
111
112
static void *app_data;
113
114
int
115
sec_getc(FILE *F)
116
{
117
if(sec_complete && data_prot) {
118
char c;
119
if(sec_read(fileno(F), &c, 1) <= 0)
120
return EOF;
121
return c;
122
} else
123
return getc(F);
124
}
125
126
static int
127
block_read(int fd, void *buf, size_t len)
128
{
129
unsigned char *p = buf;
130
int b;
131
while(len) {
132
b = read(fd, p, len);
133
if (b == 0)
134
return 0;
135
else if (b < 0)
136
return -1;
137
len -= b;
138
p += b;
139
}
140
return p - (unsigned char*)buf;
141
}
142
143
static int
144
block_write(int fd, void *buf, size_t len)
145
{
146
unsigned char *p = buf;
147
int b;
148
while(len) {
149
b = write(fd, p, len);
150
if(b < 0)
151
return -1;
152
len -= b;
153
p += b;
154
}
155
return p - (unsigned char*)buf;
156
}
157
158
static int
159
sec_get_data(int fd, struct buffer *buf, int level)
160
{
161
int len;
162
int b;
163
void *tmp;
164
165
b = block_read(fd, &len, sizeof(len));
166
if (b == 0)
167
return 0;
168
else if (b < 0)
169
return -1;
170
len = ntohl(len);
171
tmp = realloc(buf->data, len);
172
if (tmp == NULL)
173
return -1;
174
buf->data = tmp;
175
b = block_read(fd, buf->data, len);
176
if (b == 0)
177
return 0;
178
else if (b < 0)
179
return -1;
180
buf->size = (*mech->decode)(app_data, buf->data, len, data_prot);
181
buf->index = 0;
182
return 0;
183
}
184
185
static size_t
186
buffer_read(struct buffer *buf, void *dataptr, size_t len)
187
{
188
len = min(len, buf->size - buf->index);
189
memcpy(dataptr, (char*)buf->data + buf->index, len);
190
buf->index += len;
191
return len;
192
}
193
194
static size_t
195
buffer_write(struct buffer *buf, void *dataptr, size_t len)
196
{
197
if(buf->index + len > buf->size) {
198
void *tmp;
199
if(buf->data == NULL)
200
tmp = malloc(1024);
201
else
202
tmp = realloc(buf->data, buf->index + len);
203
if(tmp == NULL)
204
return -1;
205
buf->data = tmp;
206
buf->size = buf->index + len;
207
}
208
memcpy((char*)buf->data + buf->index, dataptr, len);
209
buf->index += len;
210
return len;
211
}
212
213
int
214
sec_read(int fd, void *dataptr, int length)
215
{
216
size_t len;
217
int rx = 0;
218
219
if(sec_complete == 0 || data_prot == 0)
220
return read(fd, dataptr, length);
221
222
if(in_buffer.eof_flag){
223
in_buffer.eof_flag = 0;
224
return 0;
225
}
226
227
len = buffer_read(&in_buffer, dataptr, length);
228
length -= len;
229
rx += len;
230
dataptr = (char*)dataptr + len;
231
232
while(length){
233
int ret;
234
235
ret = sec_get_data(fd, &in_buffer, data_prot);
236
if (ret < 0)
237
return -1;
238
if(ret == 0 && in_buffer.size == 0) {
239
if(rx)
240
in_buffer.eof_flag = 1;
241
return rx;
242
}
243
len = buffer_read(&in_buffer, dataptr, length);
244
length -= len;
245
rx += len;
246
dataptr = (char*)dataptr + len;
247
}
248
return rx;
249
}
250
251
static int
252
sec_send(int fd, char *from, int length)
253
{
254
int bytes;
255
void *buf;
256
bytes = (*mech->encode)(app_data, from, length, data_prot, &buf);
257
bytes = htonl(bytes);
258
block_write(fd, &bytes, sizeof(bytes));
259
block_write(fd, buf, ntohl(bytes));
260
free(buf);
261
return length;
262
}
263
264
int
265
sec_fflush(FILE *F)
266
{
267
if(data_prot != prot_clear) {
268
if(out_buffer.index > 0){
269
sec_write(fileno(F), out_buffer.data, out_buffer.index);
270
out_buffer.index = 0;
271
}
272
sec_send(fileno(F), NULL, 0);
273
}
274
fflush(F);
275
return 0;
276
}
277
278
int
279
sec_write(int fd, char *dataptr, int length)
280
{
281
int len = buffer_size;
282
int tx = 0;
283
284
if(data_prot == prot_clear)
285
return write(fd, dataptr, length);
286
287
len -= (*mech->overhead)(app_data, data_prot, len);
288
while(length){
289
if(length < len)
290
len = length;
291
sec_send(fd, dataptr, len);
292
length -= len;
293
dataptr += len;
294
tx += len;
295
}
296
return tx;
297
}
298
299
int
300
sec_vfprintf2(FILE *f, const char *fmt, va_list ap)
301
{
302
char *buf;
303
int ret;
304
if(data_prot == prot_clear)
305
return vfprintf(f, fmt, ap);
306
else {
307
int len;
308
len = vasprintf(&buf, fmt, ap);
309
if (len == -1)
310
return len;
311
ret = buffer_write(&out_buffer, buf, len);
312
free(buf);
313
return ret;
314
}
315
}
316
317
int
318
sec_fprintf2(FILE *f, const char *fmt, ...)
319
{
320
int ret;
321
va_list ap;
322
va_start(ap, fmt);
323
ret = sec_vfprintf2(f, fmt, ap);
324
va_end(ap);
325
return ret;
326
}
327
328
int
329
sec_putc(int c, FILE *F)
330
{
331
char ch = c;
332
if(data_prot == prot_clear)
333
return putc(c, F);
334
335
buffer_write(&out_buffer, &ch, 1);
336
if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
337
sec_write(fileno(F), out_buffer.data, out_buffer.index);
338
out_buffer.index = 0;
339
}
340
return c;
341
}
342
343
int
344
sec_read_msg(char *s, int level)
345
{
346
int len;
347
char *buf;
348
int return_code;
349
350
buf = malloc(strlen(s));
351
len = base64_decode(s + 4, buf); /* XXX */
352
353
len = (*mech->decode)(app_data, buf, len, level);
354
if(len < 0)
355
return -1;
356
357
buf[len] = '\0';
358
359
if(buf[3] == '-')
360
return_code = 0;
361
else
362
sscanf(buf, "%d", &return_code);
363
if(buf[len-1] == '\n')
364
buf[len-1] = '\0';
365
strcpy(s, buf);
366
free(buf);
367
return return_code;
368
}
369
370
int
371
sec_vfprintf(FILE *f, const char *fmt, va_list ap)
372
{
373
char *buf;
374
void *enc;
375
int len;
376
if(!sec_complete)
377
return vfprintf(f, fmt, ap);
378
379
if (vasprintf(&buf, fmt, ap) == -1) {
380
printf("Failed to allocate command.\n");
381
return -1;
382
}
383
len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
384
free(buf);
385
if(len < 0) {
386
printf("Failed to encode command.\n");
387
return -1;
388
}
389
if(base64_encode(enc, len, &buf) < 0){
390
free(enc);
391
printf("Out of memory base64-encoding.\n");
392
return -1;
393
}
394
free(enc);
395
#ifdef FTP_SERVER
396
if(command_prot == prot_safe)
397
fprintf(f, "631 %s\r\n", buf);
398
else if(command_prot == prot_private)
399
fprintf(f, "632 %s\r\n", buf);
400
else if(command_prot == prot_confidential)
401
fprintf(f, "633 %s\r\n", buf);
402
#else
403
if(command_prot == prot_safe)
404
fprintf(f, "MIC %s", buf);
405
else if(command_prot == prot_private)
406
fprintf(f, "ENC %s", buf);
407
else if(command_prot == prot_confidential)
408
fprintf(f, "CONF %s", buf);
409
#endif
410
free(buf);
411
return 0;
412
}
413
414
int
415
sec_fprintf(FILE *f, const char *fmt, ...)
416
{
417
va_list ap;
418
int ret;
419
va_start(ap, fmt);
420
ret = sec_vfprintf(f, fmt, ap);
421
va_end(ap);
422
return ret;
423
}
424
425
/* end common stuff */
426
427
#ifdef FTP_SERVER
428
429
int ccc_passed;
430
431
void
432
auth(char *auth_name)
433
{
434
int i;
435
void *tmp;
436
437
for(i = 0; (mech = mechs[i]) != NULL; i++){
438
if(!strcasecmp(auth_name, mech->name)){
439
tmp = realloc(app_data, mech->size);
440
if (tmp == NULL) {
441
reply(431, "Unable to accept %s at this time", mech->name);
442
return;
443
}
444
app_data = tmp;
445
446
if(mech->init && (*mech->init)(app_data) != 0) {
447
reply(431, "Unable to accept %s at this time", mech->name);
448
return;
449
}
450
if(mech->auth) {
451
(*mech->auth)(app_data);
452
return;
453
}
454
if(mech->adat)
455
reply(334, "Send authorization data.");
456
else
457
reply(234, "Authorization complete.");
458
return;
459
}
460
}
461
free (app_data);
462
app_data = NULL;
463
reply(504, "%s is unknown to me", auth_name);
464
}
465
466
void
467
adat(char *auth_data)
468
{
469
if(mech && !sec_complete) {
470
void *buf = malloc(strlen(auth_data));
471
size_t len;
472
len = base64_decode(auth_data, buf);
473
(*mech->adat)(app_data, buf, len);
474
free(buf);
475
} else
476
reply(503, "You must %sissue an AUTH first.", mech ? "re-" : "");
477
}
478
479
void pbsz(int size)
480
{
481
size_t new = size;
482
if(!sec_complete)
483
reply(503, "Incomplete security data exchange.");
484
if(mech->pbsz)
485
new = (*mech->pbsz)(app_data, size);
486
if(buffer_size != new){
487
buffer_size = size;
488
}
489
if(new != size)
490
reply(200, "PBSZ=%lu", (unsigned long)new);
491
else
492
reply(200, "OK");
493
}
494
495
void
496
prot(char *pl)
497
{
498
int p = -1;
499
500
if(buffer_size == 0){
501
reply(503, "No protection buffer size negotiated.");
502
return;
503
}
504
505
if(!strcasecmp(pl, "C"))
506
p = prot_clear;
507
else if(!strcasecmp(pl, "S"))
508
p = prot_safe;
509
else if(!strcasecmp(pl, "E"))
510
p = prot_confidential;
511
else if(!strcasecmp(pl, "P"))
512
p = prot_private;
513
else {
514
reply(504, "Unrecognized protection level.");
515
return;
516
}
517
518
if(sec_complete){
519
if((*mech->check_prot)(app_data, p)){
520
reply(536, "%s does not support %s protection.",
521
mech->name, level_to_name(p));
522
}else{
523
data_prot = (enum protection_level)p;
524
reply(200, "Data protection is %s.", level_to_name(p));
525
}
526
}else{
527
reply(503, "Incomplete security data exchange.");
528
}
529
}
530
531
void ccc(void)
532
{
533
if(sec_complete){
534
if(mech->ccc && (*mech->ccc)(app_data) == 0) {
535
command_prot = data_prot = prot_clear;
536
ccc_passed = 1;
537
} else
538
reply(534, "You must be joking.");
539
}else
540
reply(503, "Incomplete security data exchange.");
541
}
542
543
void mec(char *msg, enum protection_level level)
544
{
545
void *buf;
546
size_t len, buf_size;
547
if(!sec_complete) {
548
reply(503, "Incomplete security data exchange.");
549
return;
550
}
551
buf_size = strlen(msg) + 2;
552
buf = malloc(buf_size);
553
if (buf == NULL) {
554
reply(501, "Failed to allocate %lu", (unsigned long)buf_size);
555
return;
556
}
557
len = base64_decode(msg, buf);
558
command_prot = level;
559
if(len == (size_t)-1) {
560
free(buf);
561
reply(501, "Failed to base64-decode command");
562
return;
563
}
564
len = (*mech->decode)(app_data, buf, len, level);
565
if(len == (size_t)-1) {
566
free(buf);
567
reply(535, "Failed to decode command");
568
return;
569
}
570
((char*)buf)[len] = '\0';
571
if(strstr((char*)buf, "\r\n") == NULL)
572
strlcat((char*)buf, "\r\n", buf_size);
573
new_ftp_command(buf);
574
}
575
576
/* ------------------------------------------------------------ */
577
578
int
579
sec_userok(char *userstr)
580
{
581
if(sec_complete)
582
return (*mech->userok)(app_data, userstr);
583
return 0;
584
}
585
586
int
587
sec_session(char *user)
588
{
589
if(sec_complete && mech->session)
590
return (*mech->session)(app_data, user);
591
return 0;
592
}
593
594
char *ftp_command;
595
596
void
597
new_ftp_command(char *command)
598
{
599
ftp_command = command;
600
}
601
602
void
603
delete_ftp_command(void)
604
{
605
free(ftp_command);
606
ftp_command = NULL;
607
}
608
609
int
610
secure_command(void)
611
{
612
return ftp_command != NULL;
613
}
614
615
enum protection_level
616
get_command_prot(void)
617
{
618
return command_prot;
619
}
620
621
#else /* FTP_SERVER */
622
623
void
624
sec_status(void)
625
{
626
if(sec_complete){
627
printf("Using %s for authentication.\n", mech->name);
628
printf("Using %s command channel.\n", level_to_name(command_prot));
629
printf("Using %s data channel.\n", level_to_name(data_prot));
630
if(buffer_size > 0)
631
printf("Protection buffer size: %lu.\n",
632
(unsigned long)buffer_size);
633
}else{
634
printf("Not using any security mechanism.\n");
635
}
636
}
637
638
static int
639
sec_prot_internal(int level)
640
{
641
int ret;
642
char *p;
643
unsigned int s = 1048576;
644
645
int old_verbose = verbose;
646
verbose = 0;
647
648
if(!sec_complete){
649
printf("No security data exchange has taken place.\n");
650
return -1;
651
}
652
653
if(level){
654
ret = command("PBSZ %u", s);
655
if(ret != COMPLETE){
656
printf("Failed to set protection buffer size.\n");
657
return -1;
658
}
659
buffer_size = s;
660
p = strstr(reply_string, "PBSZ=");
661
if(p)
662
sscanf(p, "PBSZ=%u", &s);
663
if(s < buffer_size)
664
buffer_size = s;
665
}
666
verbose = old_verbose;
667
ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
668
if(ret != COMPLETE){
669
printf("Failed to set protection level.\n");
670
return -1;
671
}
672
673
data_prot = (enum protection_level)level;
674
return 0;
675
}
676
677
enum protection_level
678
set_command_prot(enum protection_level level)
679
{
680
int ret;
681
enum protection_level old = command_prot;
682
if(level != command_prot && level == prot_clear) {
683
ret = command("CCC");
684
if(ret != COMPLETE) {
685
printf("Failed to clear command channel.\n");
686
return prot_invalid;
687
}
688
}
689
command_prot = level;
690
return old;
691
}
692
693
void
694
sec_prot(int argc, char **argv)
695
{
696
int level = -1;
697
698
if(argc > 3)
699
goto usage;
700
701
if(argc == 1) {
702
sec_status();
703
return;
704
}
705
if(!sec_complete) {
706
printf("No security data exchange has taken place.\n");
707
code = -1;
708
return;
709
}
710
level = name_to_level(argv[argc - 1]);
711
712
if(level == -1)
713
goto usage;
714
715
if((*mech->check_prot)(app_data, level)) {
716
printf("%s does not implement %s protection.\n",
717
mech->name, level_to_name(level));
718
code = -1;
719
return;
720
}
721
722
if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
723
if(sec_prot_internal(level) < 0){
724
code = -1;
725
return;
726
}
727
} else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
728
if(set_command_prot(level) < 0) {
729
code = -1;
730
return;
731
}
732
} else
733
goto usage;
734
code = 0;
735
return;
736
usage:
737
printf("usage: %s [command|data] [clear|safe|confidential|private]\n",
738
argv[0]);
739
code = -1;
740
}
741
742
void
743
sec_prot_command(int argc, char **argv)
744
{
745
int level;
746
747
if(argc > 2)
748
goto usage;
749
750
if(!sec_complete) {
751
printf("No security data exchange has taken place.\n");
752
code = -1;
753
return;
754
}
755
756
if(argc == 1) {
757
sec_status();
758
} else {
759
level = name_to_level(argv[1]);
760
if(level == -1)
761
goto usage;
762
763
if((*mech->check_prot)(app_data, level)) {
764
printf("%s does not implement %s protection.\n",
765
mech->name, level_to_name(level));
766
code = -1;
767
return;
768
}
769
if(set_command_prot(level) < 0) {
770
code = -1;
771
return;
772
}
773
}
774
code = 0;
775
return;
776
usage:
777
printf("usage: %s [clear|safe|confidential|private]\n",
778
argv[0]);
779
code = -1;
780
}
781
782
static enum protection_level request_data_prot;
783
784
void
785
sec_set_protection_level(void)
786
{
787
if(sec_complete && data_prot != request_data_prot)
788
sec_prot_internal(request_data_prot);
789
}
790
791
792
int
793
sec_request_prot(char *level)
794
{
795
int l = name_to_level(level);
796
if(l == -1)
797
return -1;
798
request_data_prot = (enum protection_level)l;
799
return 0;
800
}
801
802
int
803
sec_login(char *host)
804
{
805
int ret;
806
struct sec_client_mech **m;
807
int old_verbose = verbose;
808
809
verbose = -1; /* shut up all messages this will produce (they
810
are usually not very user friendly) */
811
812
for(m = mechs; *m && (*m)->name; m++) {
813
void *tmp;
814
815
tmp = realloc(app_data, (*m)->size);
816
if (tmp == NULL) {
817
warnx ("realloc %lu failed", (unsigned long)(*m)->size);
818
return -1;
819
}
820
app_data = tmp;
821
822
if((*m)->init && (*(*m)->init)(app_data) != 0) {
823
printf("Skipping %s...\n", (*m)->name);
824
continue;
825
}
826
printf("Trying %s...\n", (*m)->name);
827
ret = command("AUTH %s", (*m)->name);
828
if(ret != CONTINUE){
829
if(code == 504){
830
printf("%s is not supported by the server.\n", (*m)->name);
831
}else if(code == 534){
832
printf("%s rejected as security mechanism.\n", (*m)->name);
833
}else if(ret == ERROR) {
834
printf("The server doesn't support the FTP "
835
"security extensions.\n");
836
verbose = old_verbose;
837
return -1;
838
}
839
continue;
840
}
841
842
ret = (*(*m)->auth)(app_data, host);
843
844
if(ret == AUTH_CONTINUE)
845
continue;
846
else if(ret != AUTH_OK){
847
/* mechanism is supposed to output error string */
848
verbose = old_verbose;
849
return -1;
850
}
851
mech = *m;
852
sec_complete = 1;
853
if(doencrypt) {
854
command_prot = prot_private;
855
request_data_prot = prot_private;
856
} else {
857
command_prot = prot_safe;
858
}
859
break;
860
}
861
862
verbose = old_verbose;
863
return *m == NULL;
864
}
865
866
void
867
sec_end(void)
868
{
869
if (mech != NULL) {
870
if(mech->end)
871
(*mech->end)(app_data);
872
if (app_data != NULL) {
873
memset(app_data, 0, mech->size);
874
free(app_data);
875
app_data = NULL;
876
}
877
}
878
sec_complete = 0;
879
data_prot = (enum protection_level)0;
880
}
881
882
#endif /* FTP_SERVER */
883
884
885