security.c revision 178825
1178825Sdfr/* 2178825Sdfr * Copyright (c) 1998-2002, 2005 Kungliga Tekniska H�gskolan 3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden). 4178825Sdfr * All rights reserved. 5178825Sdfr * 6178825Sdfr * Redistribution and use in source and binary forms, with or without 7178825Sdfr * modification, are permitted provided that the following conditions 8178825Sdfr * are met: 9178825Sdfr * 10178825Sdfr * 1. Redistributions of source code must retain the above copyright 11178825Sdfr * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 14178825Sdfr * notice, this list of conditions and the following disclaimer in the 15178825Sdfr * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors 18178825Sdfr * may be used to endorse or promote products derived from this software 19178825Sdfr * without specific prior written permission. 20178825Sdfr * 21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178825Sdfr * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#ifdef FTP_SERVER 35178825Sdfr#include "ftpd_locl.h" 36178825Sdfr#else 37178825Sdfr#include "ftp_locl.h" 38178825Sdfr#endif 39178825Sdfr 40178825SdfrRCSID("$Id: security.c 21225 2007-06-20 10:16:02Z lha $"); 41178825Sdfr 42178825Sdfrstatic enum protection_level command_prot; 43178825Sdfrstatic enum protection_level data_prot; 44178825Sdfrstatic size_t buffer_size; 45178825Sdfr 46178825Sdfrstruct buffer { 47178825Sdfr void *data; 48178825Sdfr size_t size; 49178825Sdfr size_t index; 50178825Sdfr int eof_flag; 51178825Sdfr}; 52178825Sdfr 53178825Sdfrstatic struct buffer in_buffer, out_buffer; 54178825Sdfrint sec_complete; 55178825Sdfr 56178825Sdfrstatic struct { 57178825Sdfr enum protection_level level; 58178825Sdfr const char *name; 59178825Sdfr} level_names[] = { 60178825Sdfr { prot_clear, "clear" }, 61178825Sdfr { prot_safe, "safe" }, 62178825Sdfr { prot_confidential, "confidential" }, 63178825Sdfr { prot_private, "private" } 64178825Sdfr}; 65178825Sdfr 66178825Sdfrstatic const char * 67178825Sdfrlevel_to_name(enum protection_level level) 68178825Sdfr{ 69178825Sdfr int i; 70178825Sdfr for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++) 71178825Sdfr if(level_names[i].level == level) 72178825Sdfr return level_names[i].name; 73178825Sdfr return "unknown"; 74178825Sdfr} 75178825Sdfr 76178825Sdfr#ifndef FTP_SERVER /* not used in server */ 77178825Sdfrstatic enum protection_level 78178825Sdfrname_to_level(const char *name) 79178825Sdfr{ 80178825Sdfr int i; 81178825Sdfr for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++) 82178825Sdfr if(!strncasecmp(level_names[i].name, name, strlen(name))) 83178825Sdfr return level_names[i].level; 84178825Sdfr return (enum protection_level)-1; 85178825Sdfr} 86178825Sdfr#endif 87178825Sdfr 88178825Sdfr#ifdef FTP_SERVER 89178825Sdfr 90178825Sdfrstatic struct sec_server_mech *mechs[] = { 91178825Sdfr#ifdef KRB5 92178825Sdfr &gss_server_mech, 93178825Sdfr#endif 94178825Sdfr#ifdef KRB4 95178825Sdfr &krb4_server_mech, 96178825Sdfr#endif 97178825Sdfr NULL 98178825Sdfr}; 99178825Sdfr 100178825Sdfrstatic struct sec_server_mech *mech; 101178825Sdfr 102178825Sdfr#else 103178825Sdfr 104178825Sdfrstatic struct sec_client_mech *mechs[] = { 105178825Sdfr#ifdef KRB5 106178825Sdfr &gss_client_mech, 107178825Sdfr#endif 108178825Sdfr#ifdef KRB4 109178825Sdfr &krb4_client_mech, 110178825Sdfr#endif 111178825Sdfr NULL 112178825Sdfr}; 113178825Sdfr 114178825Sdfrstatic struct sec_client_mech *mech; 115178825Sdfr 116178825Sdfr#endif 117178825Sdfr 118178825Sdfrstatic void *app_data; 119178825Sdfr 120178825Sdfrint 121178825Sdfrsec_getc(FILE *F) 122178825Sdfr{ 123178825Sdfr if(sec_complete && data_prot) { 124178825Sdfr char c; 125178825Sdfr if(sec_read(fileno(F), &c, 1) <= 0) 126178825Sdfr return EOF; 127178825Sdfr return c; 128178825Sdfr } else 129178825Sdfr return getc(F); 130178825Sdfr} 131178825Sdfr 132178825Sdfrstatic int 133178825Sdfrblock_read(int fd, void *buf, size_t len) 134178825Sdfr{ 135178825Sdfr unsigned char *p = buf; 136178825Sdfr int b; 137178825Sdfr while(len) { 138178825Sdfr b = read(fd, p, len); 139178825Sdfr if (b == 0) 140178825Sdfr return 0; 141178825Sdfr else if (b < 0) 142178825Sdfr return -1; 143178825Sdfr len -= b; 144178825Sdfr p += b; 145178825Sdfr } 146178825Sdfr return p - (unsigned char*)buf; 147178825Sdfr} 148178825Sdfr 149178825Sdfrstatic int 150178825Sdfrblock_write(int fd, void *buf, size_t len) 151178825Sdfr{ 152178825Sdfr unsigned char *p = buf; 153178825Sdfr int b; 154178825Sdfr while(len) { 155178825Sdfr b = write(fd, p, len); 156178825Sdfr if(b < 0) 157178825Sdfr return -1; 158178825Sdfr len -= b; 159178825Sdfr p += b; 160178825Sdfr } 161178825Sdfr return p - (unsigned char*)buf; 162178825Sdfr} 163178825Sdfr 164178825Sdfrstatic int 165178825Sdfrsec_get_data(int fd, struct buffer *buf, int level) 166178825Sdfr{ 167178825Sdfr int len; 168178825Sdfr int b; 169178825Sdfr void *tmp; 170178825Sdfr 171178825Sdfr b = block_read(fd, &len, sizeof(len)); 172178825Sdfr if (b == 0) 173178825Sdfr return 0; 174178825Sdfr else if (b < 0) 175178825Sdfr return -1; 176178825Sdfr len = ntohl(len); 177178825Sdfr tmp = realloc(buf->data, len); 178178825Sdfr if (tmp == NULL) 179178825Sdfr return -1; 180178825Sdfr buf->data = tmp; 181178825Sdfr b = block_read(fd, buf->data, len); 182178825Sdfr if (b == 0) 183178825Sdfr return 0; 184178825Sdfr else if (b < 0) 185178825Sdfr return -1; 186178825Sdfr buf->size = (*mech->decode)(app_data, buf->data, len, data_prot); 187178825Sdfr buf->index = 0; 188178825Sdfr return 0; 189178825Sdfr} 190178825Sdfr 191178825Sdfrstatic size_t 192178825Sdfrbuffer_read(struct buffer *buf, void *dataptr, size_t len) 193178825Sdfr{ 194178825Sdfr len = min(len, buf->size - buf->index); 195178825Sdfr memcpy(dataptr, (char*)buf->data + buf->index, len); 196178825Sdfr buf->index += len; 197178825Sdfr return len; 198178825Sdfr} 199178825Sdfr 200178825Sdfrstatic size_t 201178825Sdfrbuffer_write(struct buffer *buf, void *dataptr, size_t len) 202178825Sdfr{ 203178825Sdfr if(buf->index + len > buf->size) { 204178825Sdfr void *tmp; 205178825Sdfr if(buf->data == NULL) 206178825Sdfr tmp = malloc(1024); 207178825Sdfr else 208178825Sdfr tmp = realloc(buf->data, buf->index + len); 209178825Sdfr if(tmp == NULL) 210178825Sdfr return -1; 211178825Sdfr buf->data = tmp; 212178825Sdfr buf->size = buf->index + len; 213178825Sdfr } 214178825Sdfr memcpy((char*)buf->data + buf->index, dataptr, len); 215178825Sdfr buf->index += len; 216178825Sdfr return len; 217178825Sdfr} 218178825Sdfr 219178825Sdfrint 220178825Sdfrsec_read(int fd, void *dataptr, int length) 221178825Sdfr{ 222178825Sdfr size_t len; 223178825Sdfr int rx = 0; 224178825Sdfr 225178825Sdfr if(sec_complete == 0 || data_prot == 0) 226178825Sdfr return read(fd, dataptr, length); 227178825Sdfr 228178825Sdfr if(in_buffer.eof_flag){ 229178825Sdfr in_buffer.eof_flag = 0; 230178825Sdfr return 0; 231178825Sdfr } 232178825Sdfr 233178825Sdfr len = buffer_read(&in_buffer, dataptr, length); 234178825Sdfr length -= len; 235178825Sdfr rx += len; 236178825Sdfr dataptr = (char*)dataptr + len; 237178825Sdfr 238178825Sdfr while(length){ 239178825Sdfr int ret; 240178825Sdfr 241178825Sdfr ret = sec_get_data(fd, &in_buffer, data_prot); 242178825Sdfr if (ret < 0) 243178825Sdfr return -1; 244178825Sdfr if(ret == 0 && in_buffer.size == 0) { 245178825Sdfr if(rx) 246178825Sdfr in_buffer.eof_flag = 1; 247178825Sdfr return rx; 248178825Sdfr } 249178825Sdfr len = buffer_read(&in_buffer, dataptr, length); 250178825Sdfr length -= len; 251178825Sdfr rx += len; 252178825Sdfr dataptr = (char*)dataptr + len; 253178825Sdfr } 254178825Sdfr return rx; 255178825Sdfr} 256178825Sdfr 257178825Sdfrstatic int 258178825Sdfrsec_send(int fd, char *from, int length) 259178825Sdfr{ 260178825Sdfr int bytes; 261178825Sdfr void *buf; 262178825Sdfr bytes = (*mech->encode)(app_data, from, length, data_prot, &buf); 263178825Sdfr bytes = htonl(bytes); 264178825Sdfr block_write(fd, &bytes, sizeof(bytes)); 265178825Sdfr block_write(fd, buf, ntohl(bytes)); 266178825Sdfr free(buf); 267178825Sdfr return length; 268178825Sdfr} 269178825Sdfr 270178825Sdfrint 271178825Sdfrsec_fflush(FILE *F) 272178825Sdfr{ 273178825Sdfr if(data_prot != prot_clear) { 274178825Sdfr if(out_buffer.index > 0){ 275178825Sdfr sec_write(fileno(F), out_buffer.data, out_buffer.index); 276178825Sdfr out_buffer.index = 0; 277178825Sdfr } 278178825Sdfr sec_send(fileno(F), NULL, 0); 279178825Sdfr } 280178825Sdfr fflush(F); 281178825Sdfr return 0; 282178825Sdfr} 283178825Sdfr 284178825Sdfrint 285178825Sdfrsec_write(int fd, char *dataptr, int length) 286178825Sdfr{ 287178825Sdfr int len = buffer_size; 288178825Sdfr int tx = 0; 289178825Sdfr 290178825Sdfr if(data_prot == prot_clear) 291178825Sdfr return write(fd, dataptr, length); 292178825Sdfr 293178825Sdfr len -= (*mech->overhead)(app_data, data_prot, len); 294178825Sdfr while(length){ 295178825Sdfr if(length < len) 296178825Sdfr len = length; 297178825Sdfr sec_send(fd, dataptr, len); 298178825Sdfr length -= len; 299178825Sdfr dataptr += len; 300178825Sdfr tx += len; 301178825Sdfr } 302178825Sdfr return tx; 303178825Sdfr} 304178825Sdfr 305178825Sdfrint 306178825Sdfrsec_vfprintf2(FILE *f, const char *fmt, va_list ap) 307178825Sdfr{ 308178825Sdfr char *buf; 309178825Sdfr int ret; 310178825Sdfr if(data_prot == prot_clear) 311178825Sdfr return vfprintf(f, fmt, ap); 312178825Sdfr else { 313178825Sdfr int len; 314178825Sdfr len = vasprintf(&buf, fmt, ap); 315178825Sdfr if (len == -1) 316178825Sdfr return len; 317178825Sdfr ret = buffer_write(&out_buffer, buf, len); 318178825Sdfr free(buf); 319178825Sdfr return ret; 320178825Sdfr } 321178825Sdfr} 322178825Sdfr 323178825Sdfrint 324178825Sdfrsec_fprintf2(FILE *f, const char *fmt, ...) 325178825Sdfr{ 326178825Sdfr int ret; 327178825Sdfr va_list ap; 328178825Sdfr va_start(ap, fmt); 329178825Sdfr ret = sec_vfprintf2(f, fmt, ap); 330178825Sdfr va_end(ap); 331178825Sdfr return ret; 332178825Sdfr} 333178825Sdfr 334178825Sdfrint 335178825Sdfrsec_putc(int c, FILE *F) 336178825Sdfr{ 337178825Sdfr char ch = c; 338178825Sdfr if(data_prot == prot_clear) 339178825Sdfr return putc(c, F); 340178825Sdfr 341178825Sdfr buffer_write(&out_buffer, &ch, 1); 342178825Sdfr if(c == '\n' || out_buffer.index >= 1024 /* XXX */) { 343178825Sdfr sec_write(fileno(F), out_buffer.data, out_buffer.index); 344178825Sdfr out_buffer.index = 0; 345178825Sdfr } 346178825Sdfr return c; 347178825Sdfr} 348178825Sdfr 349178825Sdfrint 350178825Sdfrsec_read_msg(char *s, int level) 351178825Sdfr{ 352178825Sdfr int len; 353178825Sdfr char *buf; 354178825Sdfr int return_code; 355178825Sdfr 356178825Sdfr buf = malloc(strlen(s)); 357178825Sdfr len = base64_decode(s + 4, buf); /* XXX */ 358178825Sdfr 359178825Sdfr len = (*mech->decode)(app_data, buf, len, level); 360178825Sdfr if(len < 0) 361178825Sdfr return -1; 362178825Sdfr 363178825Sdfr buf[len] = '\0'; 364178825Sdfr 365178825Sdfr if(buf[3] == '-') 366178825Sdfr return_code = 0; 367178825Sdfr else 368178825Sdfr sscanf(buf, "%d", &return_code); 369178825Sdfr if(buf[len-1] == '\n') 370178825Sdfr buf[len-1] = '\0'; 371178825Sdfr strcpy(s, buf); 372178825Sdfr free(buf); 373178825Sdfr return return_code; 374178825Sdfr} 375178825Sdfr 376178825Sdfrint 377178825Sdfrsec_vfprintf(FILE *f, const char *fmt, va_list ap) 378178825Sdfr{ 379178825Sdfr char *buf; 380178825Sdfr void *enc; 381178825Sdfr int len; 382178825Sdfr if(!sec_complete) 383178825Sdfr return vfprintf(f, fmt, ap); 384178825Sdfr 385178825Sdfr if (vasprintf(&buf, fmt, ap) == -1) { 386178825Sdfr printf("Failed to allocate command.\n"); 387178825Sdfr return -1; 388178825Sdfr } 389178825Sdfr len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc); 390178825Sdfr free(buf); 391178825Sdfr if(len < 0) { 392178825Sdfr printf("Failed to encode command.\n"); 393178825Sdfr return -1; 394178825Sdfr } 395178825Sdfr if(base64_encode(enc, len, &buf) < 0){ 396178825Sdfr free(enc); 397178825Sdfr printf("Out of memory base64-encoding.\n"); 398178825Sdfr return -1; 399178825Sdfr } 400178825Sdfr free(enc); 401178825Sdfr#ifdef FTP_SERVER 402178825Sdfr if(command_prot == prot_safe) 403178825Sdfr fprintf(f, "631 %s\r\n", buf); 404178825Sdfr else if(command_prot == prot_private) 405178825Sdfr fprintf(f, "632 %s\r\n", buf); 406178825Sdfr else if(command_prot == prot_confidential) 407178825Sdfr fprintf(f, "633 %s\r\n", buf); 408178825Sdfr#else 409178825Sdfr if(command_prot == prot_safe) 410178825Sdfr fprintf(f, "MIC %s", buf); 411178825Sdfr else if(command_prot == prot_private) 412178825Sdfr fprintf(f, "ENC %s", buf); 413178825Sdfr else if(command_prot == prot_confidential) 414178825Sdfr fprintf(f, "CONF %s", buf); 415178825Sdfr#endif 416178825Sdfr free(buf); 417178825Sdfr return 0; 418178825Sdfr} 419178825Sdfr 420178825Sdfrint 421178825Sdfrsec_fprintf(FILE *f, const char *fmt, ...) 422178825Sdfr{ 423178825Sdfr va_list ap; 424178825Sdfr int ret; 425178825Sdfr va_start(ap, fmt); 426178825Sdfr ret = sec_vfprintf(f, fmt, ap); 427178825Sdfr va_end(ap); 428178825Sdfr return ret; 429178825Sdfr} 430178825Sdfr 431178825Sdfr/* end common stuff */ 432178825Sdfr 433178825Sdfr#ifdef FTP_SERVER 434178825Sdfr 435178825Sdfrint ccc_passed; 436178825Sdfr 437178825Sdfrvoid 438178825Sdfrauth(char *auth_name) 439178825Sdfr{ 440178825Sdfr int i; 441178825Sdfr void *tmp; 442178825Sdfr 443178825Sdfr for(i = 0; (mech = mechs[i]) != NULL; i++){ 444178825Sdfr if(!strcasecmp(auth_name, mech->name)){ 445178825Sdfr tmp = realloc(app_data, mech->size); 446178825Sdfr if (tmp == NULL) { 447178825Sdfr reply(431, "Unable to accept %s at this time", mech->name); 448178825Sdfr return; 449178825Sdfr } 450178825Sdfr app_data = tmp; 451178825Sdfr 452178825Sdfr if(mech->init && (*mech->init)(app_data) != 0) { 453178825Sdfr reply(431, "Unable to accept %s at this time", mech->name); 454178825Sdfr return; 455178825Sdfr } 456178825Sdfr if(mech->auth) { 457178825Sdfr (*mech->auth)(app_data); 458178825Sdfr return; 459178825Sdfr } 460178825Sdfr if(mech->adat) 461178825Sdfr reply(334, "Send authorization data."); 462178825Sdfr else 463178825Sdfr reply(234, "Authorization complete."); 464178825Sdfr return; 465178825Sdfr } 466178825Sdfr } 467178825Sdfr free (app_data); 468178825Sdfr app_data = NULL; 469178825Sdfr reply(504, "%s is unknown to me", auth_name); 470178825Sdfr} 471178825Sdfr 472178825Sdfrvoid 473178825Sdfradat(char *auth_data) 474178825Sdfr{ 475178825Sdfr if(mech && !sec_complete) { 476178825Sdfr void *buf = malloc(strlen(auth_data)); 477178825Sdfr size_t len; 478178825Sdfr len = base64_decode(auth_data, buf); 479178825Sdfr (*mech->adat)(app_data, buf, len); 480178825Sdfr free(buf); 481178825Sdfr } else 482178825Sdfr reply(503, "You must %sissue an AUTH first.", mech ? "re-" : ""); 483178825Sdfr} 484178825Sdfr 485178825Sdfrvoid pbsz(int size) 486178825Sdfr{ 487178825Sdfr size_t new = size; 488178825Sdfr if(!sec_complete) 489178825Sdfr reply(503, "Incomplete security data exchange."); 490178825Sdfr if(mech->pbsz) 491178825Sdfr new = (*mech->pbsz)(app_data, size); 492178825Sdfr if(buffer_size != new){ 493178825Sdfr buffer_size = size; 494178825Sdfr } 495178825Sdfr if(new != size) 496178825Sdfr reply(200, "PBSZ=%lu", (unsigned long)new); 497178825Sdfr else 498178825Sdfr reply(200, "OK"); 499178825Sdfr} 500178825Sdfr 501178825Sdfrvoid 502178825Sdfrprot(char *pl) 503178825Sdfr{ 504178825Sdfr int p = -1; 505178825Sdfr 506178825Sdfr if(buffer_size == 0){ 507178825Sdfr reply(503, "No protection buffer size negotiated."); 508178825Sdfr return; 509178825Sdfr } 510178825Sdfr 511178825Sdfr if(!strcasecmp(pl, "C")) 512178825Sdfr p = prot_clear; 513178825Sdfr else if(!strcasecmp(pl, "S")) 514178825Sdfr p = prot_safe; 515178825Sdfr else if(!strcasecmp(pl, "E")) 516178825Sdfr p = prot_confidential; 517178825Sdfr else if(!strcasecmp(pl, "P")) 518178825Sdfr p = prot_private; 519178825Sdfr else { 520178825Sdfr reply(504, "Unrecognized protection level."); 521178825Sdfr return; 522178825Sdfr } 523178825Sdfr 524178825Sdfr if(sec_complete){ 525178825Sdfr if((*mech->check_prot)(app_data, p)){ 526178825Sdfr reply(536, "%s does not support %s protection.", 527178825Sdfr mech->name, level_to_name(p)); 528178825Sdfr }else{ 529178825Sdfr data_prot = (enum protection_level)p; 530178825Sdfr reply(200, "Data protection is %s.", level_to_name(p)); 531178825Sdfr } 532178825Sdfr }else{ 533178825Sdfr reply(503, "Incomplete security data exchange."); 534178825Sdfr } 535178825Sdfr} 536178825Sdfr 537178825Sdfrvoid ccc(void) 538178825Sdfr{ 539178825Sdfr if(sec_complete){ 540178825Sdfr if(mech->ccc && (*mech->ccc)(app_data) == 0) { 541178825Sdfr command_prot = data_prot = prot_clear; 542178825Sdfr ccc_passed = 1; 543178825Sdfr } else 544178825Sdfr reply(534, "You must be joking."); 545178825Sdfr }else 546178825Sdfr reply(503, "Incomplete security data exchange."); 547178825Sdfr} 548178825Sdfr 549178825Sdfrvoid mec(char *msg, enum protection_level level) 550178825Sdfr{ 551178825Sdfr void *buf; 552178825Sdfr size_t len, buf_size; 553178825Sdfr if(!sec_complete) { 554178825Sdfr reply(503, "Incomplete security data exchange."); 555178825Sdfr return; 556178825Sdfr } 557178825Sdfr buf_size = strlen(msg) + 2; 558178825Sdfr buf = malloc(buf_size); 559178825Sdfr len = base64_decode(msg, buf); 560178825Sdfr command_prot = level; 561178825Sdfr if(len == (size_t)-1) { 562178825Sdfr reply(501, "Failed to base64-decode command"); 563178825Sdfr return; 564178825Sdfr } 565178825Sdfr len = (*mech->decode)(app_data, buf, len, level); 566178825Sdfr if(len == (size_t)-1) { 567178825Sdfr reply(535, "Failed to decode command"); 568178825Sdfr return; 569178825Sdfr } 570178825Sdfr ((char*)buf)[len] = '\0'; 571178825Sdfr if(strstr((char*)buf, "\r\n") == NULL) 572178825Sdfr strlcat((char*)buf, "\r\n", buf_size); 573178825Sdfr new_ftp_command(buf); 574178825Sdfr} 575178825Sdfr 576178825Sdfr/* ------------------------------------------------------------ */ 577178825Sdfr 578178825Sdfrint 579178825Sdfrsec_userok(char *userstr) 580178825Sdfr{ 581178825Sdfr if(sec_complete) 582178825Sdfr return (*mech->userok)(app_data, userstr); 583178825Sdfr return 0; 584178825Sdfr} 585178825Sdfr 586178825Sdfrint 587178825Sdfrsec_session(char *user) 588178825Sdfr{ 589178825Sdfr if(sec_complete && mech->session) 590178825Sdfr return (*mech->session)(app_data, user); 591178825Sdfr return 0; 592178825Sdfr} 593178825Sdfr 594178825Sdfrchar *ftp_command; 595178825Sdfr 596178825Sdfrvoid 597178825Sdfrnew_ftp_command(char *command) 598178825Sdfr{ 599178825Sdfr ftp_command = command; 600178825Sdfr} 601178825Sdfr 602178825Sdfrvoid 603178825Sdfrdelete_ftp_command(void) 604178825Sdfr{ 605178825Sdfr free(ftp_command); 606178825Sdfr ftp_command = NULL; 607178825Sdfr} 608178825Sdfr 609178825Sdfrint 610178825Sdfrsecure_command(void) 611178825Sdfr{ 612178825Sdfr return ftp_command != NULL; 613178825Sdfr} 614178825Sdfr 615178825Sdfrenum protection_level 616178825Sdfrget_command_prot(void) 617178825Sdfr{ 618178825Sdfr return command_prot; 619178825Sdfr} 620178825Sdfr 621178825Sdfr#else /* FTP_SERVER */ 622178825Sdfr 623178825Sdfrvoid 624178825Sdfrsec_status(void) 625178825Sdfr{ 626178825Sdfr if(sec_complete){ 627178825Sdfr printf("Using %s for authentication.\n", mech->name); 628178825Sdfr printf("Using %s command channel.\n", level_to_name(command_prot)); 629178825Sdfr printf("Using %s data channel.\n", level_to_name(data_prot)); 630178825Sdfr if(buffer_size > 0) 631178825Sdfr printf("Protection buffer size: %lu.\n", 632178825Sdfr (unsigned long)buffer_size); 633178825Sdfr }else{ 634178825Sdfr printf("Not using any security mechanism.\n"); 635178825Sdfr } 636178825Sdfr} 637178825Sdfr 638178825Sdfrstatic int 639178825Sdfrsec_prot_internal(int level) 640178825Sdfr{ 641178825Sdfr int ret; 642178825Sdfr char *p; 643178825Sdfr unsigned int s = 1048576; 644178825Sdfr 645178825Sdfr int old_verbose = verbose; 646178825Sdfr verbose = 0; 647178825Sdfr 648178825Sdfr if(!sec_complete){ 649178825Sdfr printf("No security data exchange has taken place.\n"); 650178825Sdfr return -1; 651178825Sdfr } 652178825Sdfr 653178825Sdfr if(level){ 654178825Sdfr ret = command("PBSZ %u", s); 655178825Sdfr if(ret != COMPLETE){ 656178825Sdfr printf("Failed to set protection buffer size.\n"); 657178825Sdfr return -1; 658178825Sdfr } 659178825Sdfr buffer_size = s; 660178825Sdfr p = strstr(reply_string, "PBSZ="); 661178825Sdfr if(p) 662178825Sdfr sscanf(p, "PBSZ=%u", &s); 663178825Sdfr if(s < buffer_size) 664178825Sdfr buffer_size = s; 665178825Sdfr } 666178825Sdfr verbose = old_verbose; 667178825Sdfr ret = command("PROT %c", level["CSEP"]); /* XXX :-) */ 668178825Sdfr if(ret != COMPLETE){ 669178825Sdfr printf("Failed to set protection level.\n"); 670178825Sdfr return -1; 671178825Sdfr } 672178825Sdfr 673178825Sdfr data_prot = (enum protection_level)level; 674178825Sdfr return 0; 675178825Sdfr} 676178825Sdfr 677178825Sdfrenum protection_level 678178825Sdfrset_command_prot(enum protection_level level) 679178825Sdfr{ 680178825Sdfr int ret; 681178825Sdfr enum protection_level old = command_prot; 682178825Sdfr if(level != command_prot && level == prot_clear) { 683178825Sdfr ret = command("CCC"); 684178825Sdfr if(ret != COMPLETE) { 685178825Sdfr printf("Failed to clear command channel.\n"); 686178825Sdfr return -1; 687178825Sdfr } 688178825Sdfr } 689178825Sdfr command_prot = level; 690178825Sdfr return old; 691178825Sdfr} 692178825Sdfr 693178825Sdfrvoid 694178825Sdfrsec_prot(int argc, char **argv) 695178825Sdfr{ 696178825Sdfr int level = -1; 697178825Sdfr 698178825Sdfr if(argc > 3) 699178825Sdfr goto usage; 700178825Sdfr 701178825Sdfr if(argc == 1) { 702178825Sdfr sec_status(); 703178825Sdfr return; 704178825Sdfr } 705178825Sdfr if(!sec_complete) { 706178825Sdfr printf("No security data exchange has taken place.\n"); 707178825Sdfr code = -1; 708178825Sdfr return; 709178825Sdfr } 710178825Sdfr level = name_to_level(argv[argc - 1]); 711178825Sdfr 712178825Sdfr if(level == -1) 713178825Sdfr goto usage; 714178825Sdfr 715178825Sdfr if((*mech->check_prot)(app_data, level)) { 716178825Sdfr printf("%s does not implement %s protection.\n", 717178825Sdfr mech->name, level_to_name(level)); 718178825Sdfr code = -1; 719178825Sdfr return; 720178825Sdfr } 721178825Sdfr 722178825Sdfr if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) { 723178825Sdfr if(sec_prot_internal(level) < 0){ 724178825Sdfr code = -1; 725178825Sdfr return; 726178825Sdfr } 727178825Sdfr } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) { 728178825Sdfr if(set_command_prot(level) < 0) { 729178825Sdfr code = -1; 730178825Sdfr return; 731178825Sdfr } 732178825Sdfr } else 733178825Sdfr goto usage; 734178825Sdfr code = 0; 735178825Sdfr return; 736178825Sdfr usage: 737178825Sdfr printf("usage: %s [command|data] [clear|safe|confidential|private]\n", 738178825Sdfr argv[0]); 739178825Sdfr code = -1; 740178825Sdfr} 741178825Sdfr 742178825Sdfrvoid 743178825Sdfrsec_prot_command(int argc, char **argv) 744178825Sdfr{ 745178825Sdfr int level; 746178825Sdfr 747178825Sdfr if(argc > 2) 748178825Sdfr goto usage; 749178825Sdfr 750178825Sdfr if(!sec_complete) { 751178825Sdfr printf("No security data exchange has taken place.\n"); 752178825Sdfr code = -1; 753178825Sdfr return; 754178825Sdfr } 755178825Sdfr 756178825Sdfr if(argc == 1) { 757178825Sdfr sec_status(); 758178825Sdfr } else { 759178825Sdfr level = name_to_level(argv[1]); 760178825Sdfr if(level == -1) 761178825Sdfr goto usage; 762178825Sdfr 763178825Sdfr if((*mech->check_prot)(app_data, level)) { 764178825Sdfr printf("%s does not implement %s protection.\n", 765178825Sdfr mech->name, level_to_name(level)); 766178825Sdfr code = -1; 767178825Sdfr return; 768178825Sdfr } 769178825Sdfr if(set_command_prot(level) < 0) { 770178825Sdfr code = -1; 771178825Sdfr return; 772178825Sdfr } 773178825Sdfr } 774178825Sdfr code = 0; 775178825Sdfr return; 776178825Sdfr usage: 777178825Sdfr printf("usage: %s [clear|safe|confidential|private]\n", 778178825Sdfr argv[0]); 779178825Sdfr code = -1; 780178825Sdfr} 781178825Sdfr 782178825Sdfrstatic enum protection_level request_data_prot; 783178825Sdfr 784178825Sdfrvoid 785178825Sdfrsec_set_protection_level(void) 786178825Sdfr{ 787178825Sdfr if(sec_complete && data_prot != request_data_prot) 788178825Sdfr sec_prot_internal(request_data_prot); 789178825Sdfr} 790178825Sdfr 791178825Sdfr 792178825Sdfrint 793178825Sdfrsec_request_prot(char *level) 794178825Sdfr{ 795178825Sdfr int l = name_to_level(level); 796178825Sdfr if(l == -1) 797178825Sdfr return -1; 798178825Sdfr request_data_prot = (enum protection_level)l; 799178825Sdfr return 0; 800178825Sdfr} 801178825Sdfr 802178825Sdfrint 803178825Sdfrsec_login(char *host) 804178825Sdfr{ 805178825Sdfr int ret; 806178825Sdfr struct sec_client_mech **m; 807178825Sdfr int old_verbose = verbose; 808178825Sdfr 809178825Sdfr verbose = -1; /* shut up all messages this will produce (they 810178825Sdfr are usually not very user friendly) */ 811178825Sdfr 812178825Sdfr for(m = mechs; *m && (*m)->name; m++) { 813178825Sdfr void *tmp; 814178825Sdfr 815178825Sdfr tmp = realloc(app_data, (*m)->size); 816178825Sdfr if (tmp == NULL) { 817178825Sdfr warnx ("realloc %lu failed", (unsigned long)(*m)->size); 818178825Sdfr return -1; 819178825Sdfr } 820178825Sdfr app_data = tmp; 821178825Sdfr 822178825Sdfr if((*m)->init && (*(*m)->init)(app_data) != 0) { 823178825Sdfr printf("Skipping %s...\n", (*m)->name); 824178825Sdfr continue; 825178825Sdfr } 826178825Sdfr printf("Trying %s...\n", (*m)->name); 827178825Sdfr ret = command("AUTH %s", (*m)->name); 828178825Sdfr if(ret != CONTINUE){ 829178825Sdfr if(code == 504){ 830178825Sdfr printf("%s is not supported by the server.\n", (*m)->name); 831178825Sdfr }else if(code == 534){ 832178825Sdfr printf("%s rejected as security mechanism.\n", (*m)->name); 833178825Sdfr }else if(ret == ERROR) { 834178825Sdfr printf("The server doesn't support the FTP " 835178825Sdfr "security extensions.\n"); 836178825Sdfr verbose = old_verbose; 837178825Sdfr return -1; 838178825Sdfr } 839178825Sdfr continue; 840178825Sdfr } 841178825Sdfr 842178825Sdfr ret = (*(*m)->auth)(app_data, host); 843178825Sdfr 844178825Sdfr if(ret == AUTH_CONTINUE) 845178825Sdfr continue; 846178825Sdfr else if(ret != AUTH_OK){ 847178825Sdfr /* mechanism is supposed to output error string */ 848178825Sdfr verbose = old_verbose; 849178825Sdfr return -1; 850178825Sdfr } 851178825Sdfr mech = *m; 852178825Sdfr sec_complete = 1; 853178825Sdfr if(doencrypt) { 854178825Sdfr command_prot = prot_private; 855178825Sdfr request_data_prot = prot_private; 856178825Sdfr } else { 857178825Sdfr command_prot = prot_safe; 858178825Sdfr } 859178825Sdfr break; 860178825Sdfr } 861178825Sdfr 862178825Sdfr verbose = old_verbose; 863178825Sdfr return *m == NULL; 864178825Sdfr} 865178825Sdfr 866178825Sdfrvoid 867178825Sdfrsec_end(void) 868178825Sdfr{ 869178825Sdfr if (mech != NULL) { 870178825Sdfr if(mech->end) 871178825Sdfr (*mech->end)(app_data); 872178825Sdfr if (app_data != NULL) { 873178825Sdfr memset(app_data, 0, mech->size); 874178825Sdfr free(app_data); 875178825Sdfr app_data = NULL; 876178825Sdfr } 877178825Sdfr } 878178825Sdfr sec_complete = 0; 879178825Sdfr data_prot = (enum protection_level)0; 880178825Sdfr} 881178825Sdfr 882178825Sdfr#endif /* FTP_SERVER */ 883178825Sdfr 884