1 /*
   2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 #pragma ident   "%Z%%M% %I%     %E% SMI"
   7 
   8 /*
   9  * gssutil.c
  10  *
  11  * Utility routines for providing security related services to
  12  * the FTP server.  This code uses the GSSAPI (RFC 2743, 2744)
  13  * to provide a generic security layer to the application.  The
  14  * security mechanism providing the actual security functions
  15  * is abstracted from the application itself.  In the case of the FTP
  16  * server, the security mechanism is based on what the client chooses
  17  * to use when it makes the secure connection.  If the client's
  18  * choice of GSS mechanism is not supported by the FTP server, the
  19  * connection may be rejected or fall back to standard Unix/PAM
  20  * authentication.
  21  *
  22  * This code is primarily intended to work with clients who choose
  23  * the Kerberos V5 GSSAPI mechanism as their security service.
  24  */
  25 
  26 #include "config.h"
  27 
  28 #if defined(USE_GSS)
  29 #include <stdio.h>
  30 #include <string.h>
  31 #include <sys/types.h>
  32 #include <ctype.h>
  33 #include <stdlib.h>
  34 #include <signal.h>
  35 #include <pwd.h>
  36 
  37 #include <netinet/in.h>
  38 #include <netinet/in_systm.h>
  39 #include <netinet/ip.h>
  40 
  41 #include <errno.h>
  42 #include <sys/param.h>
  43 #include <netdb.h>
  44 #ifdef HAVE_SYS_SYSLOG_H
  45 #include <sys/syslog.h>
  46 #endif
  47 
  48 /* CSTYLED */
  49 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
  50 #include <syslog.h>
  51 #endif
  52 
  53 #ifdef HAVE_SYSINFO
  54 #include <sys/systeminfo.h>
  55 #endif
  56 
  57 #include <arpa/ftp.h>
  58 
  59 #include "gssutil.h"
  60 #include "proto.h"
  61 
  62 static char *gss_services[] = { "ftp", "host", 0 };
  63 
  64 gss_info_t gss_info = {
  65         /* context */ GSS_C_NO_CONTEXT,
  66         /* mechoid */ GSS_C_NULL_OID,
  67         /* client */  NULL,
  68         /* display_name */ NULL,
  69         /* data_prot */  PROT_C,
  70         /* ctrl_prot */  PROT_C,
  71         /* authstate */  GSS_AUTH_NONE,
  72         /* want_creds */ 0,
  73         /* have_creds */ 0,
  74         /* must_auth  */ 0
  75 };
  76 
  77 
  78 extern char *cur_auth_type;
  79 extern struct SOCKSTORAGE his_addr;
  80 extern struct SOCKSTORAGE ctrl_addr;
  81 extern int debug;
  82 
  83 static char *radixN =
  84         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  85 
  86 static char pad = '=';
  87 
  88 #define DEF_GSSBUF_SIZE 2028
  89 #define DECODELEN(l)            (((3 * (l)) / 4) + 4)
  90 #define ENCODELEN(l)            (((4 * (l)) / 3) + 4)
  91 
  92 typedef struct {
  93         char   *buf;
  94         size_t alloc_len;
  95         size_t len;  /* max length of buffer */
  96         size_t idx;  /* offset to beginning of read/write data */
  97         size_t clen;  /* length of the remaining, decrypted data from client */
  98 }bufrec;
  99 
 100 static bufrec obr = {NULL, 0, 0, 0, 0};
 101 static bufrec ibr = {NULL, 0, 0, 0, 0};
 102 
 103 static int looping_write(int fd, const char *buf, size_t len);
 104 static int looping_read(int fd, char *buf, size_t len);
 105 static int radix_encode(unsigned char *inbuf, unsigned char *outbuf,
 106                         size_t len, int *outlen, int decode);
 107 static char *radix_error(int e);
 108 static void reply_gss_error(int code, OM_uint32 maj_stat,
 109                         OM_uint32 min_stat, gss_OID mechoid, char *s);
 110 static void cleanup_bufrec(bufrec *brec);
 111 static int alloc_bufrec(bufrec *brec, size_t newsz);
 112 static int sec_putbuf(int fd, unsigned char *buf, int len);
 113 static int sec_getbytes(int fd, char *buf, int nbytes);
 114 
 115 /*
 116  * Provide a routine so that ftpd can know the max amount to read
 117  */
 118 size_t
 119 gss_getinbufsz(void) {
 120         return (ibr.len);
 121 }
 122 
 123 /*
 124  * gss_adjust_buflen
 125  *
 126  * Called when the protection method changes so we can adjust the
 127  * "useable" length of our output buffer accordingly.
 128  */
 129 void
 130 gss_adjust_buflen()
 131 {
 132         OM_uint32 maj_stat, min_stat, mlen;
 133 
 134         /*
 135          * If we switched to CLEAR protection, we can use the entire buffer
 136          */
 137         if (gss_info.data_prot == PROT_C) {
 138                 obr.len = obr.alloc_len;
 139                 return;
 140         }
 141 
 142         /*
 143          * Otherwise, determine the maximum size that will allow for
 144          * the GSSAPI overhead to fit into the buffer size.
 145          */
 146         maj_stat = gss_wrap_size_limit(&min_stat, gss_info.context,
 147                                         (gss_info.data_prot == PROT_P),
 148                                         GSS_C_QOP_DEFAULT,
 149                                         (OM_uint32)obr.alloc_len, &mlen);
 150         if (maj_stat != GSS_S_COMPLETE) {
 151                         reply_gss_error(535, maj_stat, min_stat,
 152                                         gss_info.mechoid,
 153                                         "GSSAPI fudge determination");
 154                         return;
 155         }
 156         obr.len = mlen;
 157 
 158         if (debug)
 159                 syslog(LOG_DEBUG, "GSSAPI alloc_len = %d len = %d",
 160                     obr.alloc_len, obr.len);
 161 }
 162 
 163 static int
 164 looping_write(int fd, const char *buf, size_t len)
 165 {
 166         int cc;
 167         register size_t wrlen = len;
 168 
 169         do {
 170                 cc = write(fd, buf, wrlen);
 171                 if (cc < 0) {
 172                         if (errno == EINTR)
 173                                 continue;
 174                         return (cc);
 175                 } else {
 176                         buf += cc;
 177                         wrlen -= cc;
 178                 }
 179         } while (wrlen > 0);
 180 
 181         return (len);
 182 }
 183 
 184 static int
 185 looping_read(int fd, char *buf, size_t len)
 186 {
 187         int cc;
 188         size_t len2 = 0;
 189 
 190         do {
 191                 cc = read(fd, buf, len);
 192                 if (cc < 0) {
 193                         if (errno == EINTR)
 194                                 continue;
 195                         return (cc);             /* errno is already set */
 196                 } else if (cc == 0) {
 197                         return (len2);
 198                 } else {
 199                         buf += cc;
 200                         len2 += cc;
 201                         len -= cc;
 202                 }
 203         } while (len > 0);
 204         return (len2);
 205 }
 206 
 207 static int
 208 radix_encode(unsigned char *inbuf, unsigned char *outbuf,
 209                 size_t buflen, int *outlen, int decode)
 210 {
 211         register int i, j, D;
 212         char *p;
 213         unsigned char c;
 214 
 215         if (decode) {
 216                 for (i = 0, j = 0; (j < buflen) &&
 217                     inbuf[i] && inbuf[i] != pad; i++) {
 218                         if ((p = strchr(radixN, inbuf[i])) == NULL)
 219                                 return (1);
 220                         D = p - radixN;
 221                         switch (i&3) {
 222                         case 0:
 223                                 outbuf[j] = D <<2;
 224                                 break;
 225                         case 1:
 226                                 outbuf[j++] |= D >>4;
 227                                 outbuf[j] = (D&15)<<4;
 228                                 break;
 229                         case 2:
 230                                 outbuf[j++] |= D >>2;
 231                                 outbuf[j] = (D&3)<<6;
 232                                 break;
 233                         case 3:
 234                                 outbuf[j++] |= D;
 235                         }
 236                 }
 237                 if (j == buflen && (inbuf[i] && inbuf[i] != pad)) {
 238                         /* Oops, we ran out of space in the output buffer */
 239                         return (4);
 240                 }
 241                 switch (i&3) {
 242                 case 1:
 243                         return (3);
 244                 case 2: if (D&15)
 245                                 return (3);
 246                         if (strcmp((char *)&inbuf[i], "=="))
 247                                 return (2);
 248                         break;
 249                 case 3: if (D&3)
 250                                 return (3);
 251                         if (strcmp((char *)&inbuf[i], "="))
 252                                 return (2);
 253                 }
 254                 *outlen = j;
 255         } else {
 256                 for (i = 0, j = 0; i < *outlen && j < buflen; i++)
 257                         switch (i%3) {
 258                         case 0:
 259                                 outbuf[j++] = radixN[inbuf[i]>>2];
 260                                 c = (inbuf[i]&3)<<4;
 261                                 break;
 262                         case 1:
 263                                 outbuf[j++] = radixN[c|inbuf[i]>>4];
 264                                 c = (inbuf[i]&15)<<2;
 265                                 break;
 266                         case 2:
 267                                 outbuf[j++] = radixN[c|inbuf[i]>>6];
 268                                 outbuf[j++] = radixN[inbuf[i]&63];
 269                                 c = 0;
 270                 }
 271                 if (j == buflen && i < *outlen) {
 272                         /* output buffer is not big enough */
 273                         return (4);
 274                 }
 275 
 276                 if (i%3) outbuf[j++] = radixN[c];
 277                 switch (i%3) {
 278                 case 1: outbuf[j++] = pad;
 279                 case 2: outbuf[j++] = pad;
 280                 }
 281                 outbuf[*outlen = j] = '\0';
 282         }
 283         return (0);
 284 }
 285 
 286 static char *
 287 radix_error(int e)
 288 {
 289         switch (e) {
 290                 case 0:  return ("Success");
 291                 case 1:  return ("Bad character in encoding");
 292                 case 2:  return ("Encoding not properly padded");
 293                 case 3:  return ("Decoded # of bits not a multiple of 8");
 294                 case 4:  return ("Buffer size error");
 295                 default: return ("Unknown error");
 296         }
 297 }
 298 
 299 static void
 300 reply_gss_error(int code, OM_uint32 maj_stat,
 301         OM_uint32 min_stat, gss_OID mechoid, char *s)
 302 {
 303         /* a lot of work just to report the error */
 304         OM_uint32 gmaj_stat, gmin_stat;
 305         gss_buffer_desc msg;
 306         int msg_ctx;
 307         msg_ctx = 0;
 308 
 309         gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
 310                                         GSS_C_GSS_CODE,
 311                                         mechoid,
 312                                         (OM_uint32 *)&msg_ctx, &msg);
 313         if (gmaj_stat == GSS_S_COMPLETE) {
 314                 lreply(code, "GSSAPI error major: %s",
 315                         (char *)msg.value);
 316                         (void) gss_release_buffer(&gmin_stat, &msg);
 317         }
 318 
 319         gmaj_stat = gss_display_status(&gmin_stat, min_stat,
 320                                         GSS_C_MECH_CODE,
 321                                         mechoid,
 322                                         (OM_uint32 *)&msg_ctx, &msg);
 323         if (gmaj_stat == GSS_S_COMPLETE) {
 324                 lreply(code, "GSSAPI error minor: %s", (char *)msg.value);
 325                                 (void) gss_release_buffer(&gmin_stat, &msg);
 326         }
 327 
 328         reply(code, "GSSAPI error: %s", s);
 329 }
 330 
 331 
 332 static void
 333 log_status(char *msg,
 334         OM_uint32 status_code,
 335         int status_type)
 336 {
 337         OM_uint32 message_context;
 338         gss_buffer_desc status_string;
 339         OM_uint32 maj_status;
 340         OM_uint32 min_status;
 341 
 342         /* From RFC2744: */
 343         message_context = 0;
 344 
 345         do {
 346                 maj_status = gss_display_status(
 347                         &min_status,
 348                         status_code,
 349                         status_type,
 350                         GSS_C_NO_OID,
 351                         &message_context,
 352                         &status_string);
 353 
 354                 if (maj_status == GSS_S_COMPLETE) {
 355                         syslog(LOG_ERR,
 356                             "GSSAPI Error %s: %.*s\n",
 357                             msg ? msg : "<null>",
 358                             (int)status_string.length,
 359                             (char *)status_string.value);
 360 
 361                         (void) gss_release_buffer(&min_status,
 362                                                 &status_string);
 363                 } else {
 364                         syslog(LOG_ERR,
 365                 "log_status internal error: gss_display_status failed");
 366                         return;
 367                 }
 368         } while (message_context != 0);
 369 
 370 }
 371 
 372 static void
 373 log_gss_error(char *msg,
 374             OM_uint32 maj_stat,
 375             OM_uint32 min_stat)
 376 {
 377         log_status(msg, maj_stat, GSS_C_GSS_CODE);
 378         log_status(msg, min_stat, GSS_C_MECH_CODE);
 379 }
 380 
 381 
 382 static void
 383 log_gss_info(int priority,
 384             char *luser,
 385             char *remprinc,
 386             gss_OID mechoid,
 387             char *s)
 388 {
 389         const char *mechStr = __gss_oid_to_mech(mechoid);
 390 
 391         syslog(priority,
 392             "%s: local user=`%s', remote princ=`%s', mech=%s",
 393             s ? s : "<null>",
 394             luser ? luser : "<null>",
 395             remprinc ? remprinc : "<unknown>",
 396             mechStr ? mechStr : "<unknown>");
 397 }
 398 
 399 /*
 400  * gss_user
 401  *
 402  * Handle USER command after AUTH GSSAPI
 403  *
 404  * Check if the remote user can login to the local system w/out a passwd.
 405  * Use the Solaris (private) interface (__gss_userok) if possible, else do
 406  * a basic GSS-API compare.
 407  *
 408  * return 0 == BAD
 409  *        1 == OK
 410  */
 411 int
 412 gss_user(struct passwd *user_pw)
 413 {
 414         int retval = 0;
 415         OM_uint32 status, minor;
 416 
 417 #ifdef SOLARIS_GSS_USEROK
 418 
 419         int user_ok = 0;
 420 
 421         if (debug)
 422                 log_gss_info(LOG_DEBUG,
 423                             user_pw->pw_name, gss_info.display_name,
 424                             gss_info.mechoid,
 425                             "gss_user: start (gss_userok)");
 426 
 427         /* gss_auth_rules(5) */
 428         status = __gss_userok(&minor, gss_info.client,
 429                             user_pw->pw_name, &user_ok);
 430         if (status == GSS_S_COMPLETE) {
 431                 if (user_ok) {
 432                         retval = 1;  /* remote user is a-ok */
 433                 }
 434         }
 435 
 436 #else /* SOLARIS_GSS_USEROK */
 437 
 438         gss_name_t imported_name;
 439         gss_name_t canon_name;
 440         gss_buffer_desc gss_user;
 441         OM_uint32 tmpMinor;
 442         int match = 0;
 443 
 444         if (debug)
 445                 log_gss_info(LOG_DEBUG,
 446                             user_pw->pw_name, gss_info.display_name,
 447                             gss_info.mechoid, "gss_user: start");
 448 
 449         gss_user.value = user_pw->pw_name;
 450         gss_user.length = strlen(gss_user.value);
 451 
 452         status = gss_import_name(&minor,
 453                                 &gss_user,
 454                                 GSS_C_NT_USER_NAME,
 455                                 &imported_name);
 456         if (status != GSS_S_COMPLETE) {
 457                 goto out;
 458         }
 459 
 460         status = gss_canonicalize_name(&minor,
 461                                 imported_name,
 462                                 gss_info.mechoid,
 463                                 &canon_name);
 464         if (status != GSS_S_COMPLETE) {
 465                 (void) gss_release_name(&tmpMinor, &imported_name);
 466                 goto out;
 467         }
 468 
 469         status = gss_compare_name(&minor,
 470                                 canon_name,
 471                                 gss_info.client,
 472                                 &match);
 473         (void) gss_release_name(&tmpMinor, &canon_name);
 474         (void) gss_release_name(&tmpMinor, &imported_name);
 475         if (status == GSS_S_COMPLETE) {
 476                 if (match) {
 477                         retval = 1; /* remote user is a-ok */
 478                 }
 479         }
 480 
 481 out:
 482 
 483 #endif /* SOLARIS_GSS_USEROK */
 484 
 485         if (status != GSS_S_COMPLETE) {
 486                 log_gss_info(LOG_ERR, user_pw->pw_name,
 487                             gss_info.display_name, gss_info.mechoid,
 488                             "gss_user failed");
 489                 log_gss_error("gss_user failed", status, minor);
 490         }
 491 
 492         if (debug)
 493                 syslog(LOG_DEBUG, "gss_user: end: retval=%d", retval);
 494 
 495         return (retval);
 496 }
 497 
 498 
 499 /*
 500  * gss_adat
 501  *
 502  * Handle ADAT(Authentication Data) command data.
 503  */
 504 int
 505 gss_adat(char *adatstr)
 506 {
 507         int kerror, length;
 508         int replied = 0;
 509         int ret_flags;
 510         gss_buffer_desc tok, out_tok;
 511         gss_cred_id_t deleg_creds = NULL;
 512         OM_uint32 accept_maj, accept_min;
 513         OM_uint32 stat_maj, stat_min;
 514         uchar_t *gout_buf;
 515         size_t outlen;
 516 
 517         length = strlen(adatstr);
 518         outlen = DECODELEN(length);
 519 
 520         gout_buf = (uchar_t *)malloc(outlen);
 521         if (gout_buf == NULL) {
 522                 reply(501, "Couldn't decode ADAT, not enough memory");
 523                 syslog(LOG_ERR, "Couldn't decode ADAT, not enough memory");
 524                 return (0);
 525         }
 526 
 527         if ((kerror = radix_encode((unsigned char *)adatstr,
 528                                 (unsigned char *)gout_buf,
 529                                 outlen, &length, 1))) {
 530                 reply(501, "Couldn't decode ADAT(%s)",
 531                     radix_error(kerror));
 532                 syslog(LOG_ERR, "Couldn't decode ADAT(%s)",
 533                     radix_error(kerror));
 534                 return (0);
 535         }
 536         tok.value = gout_buf;
 537         tok.length = length;
 538 
 539         gss_info.context = GSS_C_NO_CONTEXT;
 540 
 541         /*
 542          * Call accept_sec_context w/GSS_C_NO_CREDENTIAL to request
 543          * default cred and to not limit the service name to one name
 544          * but rather accept what the clnt requests if service
 545          * princ/keys are available.
 546          */
 547         if (debug)
 548                 syslog(LOG_DEBUG,
 549                     "gss_adat: accept_sec_context will try default cred");
 550 
 551         out_tok.value = NULL;
 552         out_tok.length = 0;
 553 
 554         accept_maj = gss_accept_sec_context(&accept_min,
 555                                             &gss_info.context,
 556                                             GSS_C_NO_CREDENTIAL,
 557                                             &tok, /* ADAT data */
 558                                             GSS_C_NO_CHANNEL_BINDINGS,
 559                                             &gss_info.client,
 560                                             &gss_info.mechoid,
 561                                             &out_tok, /* output_token */
 562                                             (unsigned int *)&ret_flags,
 563                                             NULL, /* ignore time_rec */
 564                                             NULL); /* delegated creds */
 565 
 566 
 567         if (debug) {
 568                 if (accept_maj == GSS_S_COMPLETE)
 569                         syslog(LOG_DEBUG,
 570                             "gss_adat: accept_maj = GSS_S_COMPLETE");
 571                 else if (accept_maj == GSS_S_CONTINUE_NEEDED)
 572                         syslog(LOG_DEBUG,
 573                             "gss_adat: accept_maj = GSS_S_CONTINUE_NEEDED");
 574         }
 575         free(gout_buf);
 576 
 577         if (accept_maj != GSS_S_COMPLETE &&
 578             accept_maj != GSS_S_CONTINUE_NEEDED) {
 579                 reply_gss_error(535, accept_maj, accept_min,
 580                                 GSS_C_NO_OID, "accepting context");
 581                 syslog(LOG_ERR, "failed accepting context");
 582                 if ((ret_flags & GSS_C_DELEG_FLAG) &&
 583                     deleg_creds != NULL)
 584                         (void) gss_release_cred(&stat_min,
 585                                                 &deleg_creds);
 586 
 587                 (void) gss_release_buffer(&stat_min, &out_tok);
 588                 return (0);
 589         }
 590 
 591         if (debug)
 592                 syslog(LOG_DEBUG, "gss_adat: out_tok.length=%d",
 593                         out_tok.length);
 594         if (out_tok.length) {
 595                 size_t buflen = ENCODELEN(out_tok.length);
 596                 uchar_t *gbuf = (uchar_t *)malloc(buflen);
 597                 if (gbuf == NULL) {
 598                         reply(535, "Couldn't encode ADAT reply, "
 599                             "not enough memory.");
 600                         syslog(LOG_ERR, "Couldn't encode ADAT reply, "
 601                             "not enough memory.");
 602                         (void) gss_release_buffer(&stat_min, &out_tok);
 603                         return (0);
 604                 }
 605                 if ((kerror = radix_encode(out_tok.value,
 606                                         (unsigned char *)gbuf,
 607                                         buflen, (int *)&out_tok.length,
 608                                         0))) {
 609                         reply(535, "Couldn't encode ADAT reply(%s)",
 610                             radix_error(kerror));
 611                         syslog(LOG_ERR, "couldn't encode ADAT reply");
 612                         if ((ret_flags & GSS_C_DELEG_FLAG) &&
 613                                 deleg_creds != NULL)
 614                                 (void) gss_release_cred(&stat_min,
 615                                                         &deleg_creds);
 616 
 617                         (void) gss_release_buffer(&stat_min, &out_tok);
 618                         free(gbuf);
 619                         return (0);
 620                 }
 621 
 622                 if (accept_maj == GSS_S_COMPLETE) {
 623                         reply(235, "ADAT=%s", gbuf);
 624                         replied = 1;
 625                 } else {
 626                         /*
 627                          * If the server accepts the security data, and
 628                          * requires additional data, it should respond
 629                          * with reply code 335.
 630                          */
 631                         reply(335, "ADAT=%s", gbuf);
 632                 }
 633                 free(gbuf);
 634                 (void) gss_release_buffer(&stat_min, &out_tok);
 635         }
 636         if (accept_maj == GSS_S_COMPLETE) {
 637                 gss_buffer_desc namebuf;
 638                 gss_OID out_oid;
 639 
 640                 /* GSSAPI authentication succeeded */
 641                 gss_info.authstate = GSS_ADAT_DONE;
 642                 (void) alloc_bufrec(&obr, DEF_GSSBUF_SIZE);
 643                 (void) alloc_bufrec(&ibr, DEF_GSSBUF_SIZE);
 644                 /*
 645                  * RFC 2228 - "..., once a security data exchange completes
 646                  * successfully, if the security mechanism supports
 647                  * integrity, then integrity(via the MIC or ENC command,
 648                  * and 631 or 632 reply) must be used, ..."
 649                  */
 650                 gss_info.ctrl_prot = PROT_S;
 651 
 652                 stat_maj = gss_display_name(&stat_min, gss_info.client,
 653                                             &namebuf, &out_oid);
 654                 if (stat_maj != GSS_S_COMPLETE) {
 655                         /*
 656                          * RFC 2228 -
 657                          * "If the server rejects the security data(if
 658                          * a checksum fails, for instance), it should
 659                          * respond with reply code 535."
 660                          */
 661                         reply_gss_error(535, stat_maj, stat_min,
 662                                         gss_info.mechoid,
 663                                         "extracting GSSAPI identity name");
 664                         syslog(LOG_ERR, "gssapi error extracting identity");
 665                         if ((ret_flags & GSS_C_DELEG_FLAG) &&
 666                             deleg_creds != NULL)
 667                                 (void) gss_release_cred(&stat_min,
 668                                                         &deleg_creds);
 669                         return (0);
 670                 }
 671                 gss_info.display_name = (char *)namebuf.value;
 672 
 673                 if (ret_flags & GSS_C_DELEG_FLAG) {
 674                         gss_info.have_creds = 1;
 675                         if (deleg_creds != NULL)
 676                                 (void) gss_release_cred(&stat_min,
 677                                                         &deleg_creds);
 678                 }
 679 
 680                 /*
 681                  * If the server accepts the security data, but does
 682                  * not require any additional data(i.e., the security
 683                  * data exchange has completed successfully), it must
 684                  * respond with reply code 235.
 685                  */
 686                 if (!replied) {
 687                         if ((ret_flags & GSS_C_DELEG_FLAG) &&
 688                             !gss_info.have_creds)
 689                                 reply(235,
 690                                     "GSSAPI Authentication succeeded, but "
 691                                     "could not accept forwarded credentials");
 692                         else
 693                                 reply(235, "GSSAPI Authentication succeeded");
 694                 }
 695                 return (1);
 696         } else if (accept_maj == GSS_S_CONTINUE_NEEDED) {
 697                 /*
 698                  * If the server accepts the security data, and
 699                  * requires additional data, it should respond with
 700                  * reply code 335.
 701                  */
 702                 reply(335, "more data needed");
 703                 if ((ret_flags & GSS_C_DELEG_FLAG) &&
 704                     deleg_creds != NULL)
 705                         (void) gss_release_cred(&stat_min, &deleg_creds);
 706         }
 707 
 708         return (0);
 709 }
 710 
 711 /*
 712  * cleanup_bufrec
 713  *
 714  * cleanup the secure buffers
 715  */
 716 static void
 717 cleanup_bufrec(bufrec *brec)
 718 {
 719         if (brec->buf)
 720                 free(brec->buf);
 721         brec->len = 0;
 722         brec->clen = 0;
 723         brec->idx = 0;
 724 }
 725 
 726 static int
 727 alloc_bufrec(bufrec *brec, size_t newsz)
 728 {
 729         /*
 730          * Try to allocate a buffer, if it fails,
 731          * divide by 2 and try again.
 732          */
 733         cleanup_bufrec(brec);
 734 
 735         while (newsz > 0 && !(brec->buf = malloc(newsz))) {
 736                 syslog(LOG_ERR,
 737                     "malloc bufrec(%d bytes) failed, trying %d",
 738                     newsz >>= 1);
 739         }
 740 
 741         if (brec->buf == NULL)
 742                 return (-1);
 743 
 744         brec->alloc_len = newsz;
 745         brec->len = newsz;
 746         brec->clen = 0;
 747         brec->idx = 0;
 748         return (0);
 749 }
 750 
 751 /*
 752  * Handle PBSZ command data, return value to caller.
 753  * RFC 2228 says this is a 32 bit int, so limit max value here.
 754  */
 755 unsigned int
 756 gss_setpbsz(char *pbszstr)
 757 {
 758         unsigned int newsz = 0;
 759         char *endp;
 760 #define MAX_PBSZ 4294967295U
 761 
 762         errno = 0;
 763         newsz = (unsigned int)strtol(pbszstr, &endp, 10);
 764         if (errno != 0 || newsz > MAX_PBSZ || *endp != '\0') {
 765                 reply(501, "Bad value for PBSZ: %s", pbszstr);
 766                 return (0);
 767         }
 768 
 769         if (newsz > ibr.len) {
 770                 if (alloc_bufrec(&obr, newsz) == -1) {
 771                         perror_reply(421, "Local resource failure: malloc");
 772                         dologout(1);
 773                 }
 774                 if (alloc_bufrec(&ibr, newsz) == -1) {
 775                         perror_reply(421, "Local resource failure: malloc");
 776                         dologout(1);
 777                 }
 778         }
 779         reply(200, "PBSZ =%lu", ibr.len);
 780 
 781         return (ibr.len);
 782 }
 783 
 784 /*
 785  * sec_putbuf
 786  *
 787  * Wrap the plaintext 'buf' data using gss_wrap and send
 788  * it out.
 789  *
 790  * returns:
 791  *    bytes written (success)
 792  *   -1 on error(errno set)
 793  *   -2 on security error
 794  */
 795 static int
 796 sec_putbuf(int fd, unsigned char *buf, int len)
 797 {
 798         unsigned long net_len;
 799         int ret = 0;
 800         gss_buffer_desc in_buf, out_buf;
 801         OM_uint32 maj_stat, min_stat;
 802         int conf_state;
 803 
 804         in_buf.value = buf;
 805         in_buf.length = len;
 806         maj_stat = gss_wrap(&min_stat, gss_info.context,
 807                             (gss_info.data_prot == PROT_P),
 808                             GSS_C_QOP_DEFAULT,
 809                             &in_buf, &conf_state,
 810                             &out_buf);
 811 
 812         if (maj_stat != GSS_S_COMPLETE) {
 813                 reply_gss_error(535, maj_stat, min_stat,
 814                                 gss_info.mechoid,
 815                                 gss_info.data_prot == PROT_P ?
 816                                 "GSSAPI wrap failed":
 817                                 "GSSAPI sign failed");
 818                 return (-2);
 819         }
 820 
 821         net_len = (unsigned long)htonl((unsigned long) out_buf.length);
 822 
 823         if ((ret = looping_write(fd, (const char *)&net_len, 4)) != 4) {
 824                 syslog(LOG_ERR, "Error writing net_len(%d): %m", net_len);
 825                 ret = -1;
 826                 goto putbuf_done;
 827         }
 828 
 829         if ((ret = looping_write(fd, out_buf.value, out_buf.length)) !=
 830                 out_buf.length) {
 831                 syslog(LOG_ERR, "Error writing %d bytes: %m", out_buf.length);
 832                 ret = -1;
 833                 goto putbuf_done;
 834         }
 835 putbuf_done:
 836 
 837         gss_release_buffer(&min_stat, &out_buf);
 838         return (ret);
 839 }
 840 
 841 /*
 842  * sec_write
 843  *
 844  * If GSSAPI security is established, encode the output
 845  * and write it to the client.  Else, just write it directly.
 846  */
 847 int
 848 sec_write(int fd, char *buf, int len)
 849 {
 850         int nbytes = 0;
 851         if (gss_info.data_prot == PROT_C ||
 852             !IS_GSSAUTH(cur_auth_type) ||
 853             !(gss_info.authstate & GSS_ADAT_DONE))
 854                 nbytes = write(fd, buf, len);
 855         else {
 856                 /*
 857                  * Fill up the buffer before actually encrypting
 858                  * and writing it out.
 859                  */
 860                 while ((obr.idx < obr.len) && (len > 0)) {
 861                         int n, ret;
 862 
 863                         /* how many bytes can we fit into the buffer? */
 864                         n = (len < (obr.len - obr.idx) ? len :
 865                             obr.len - obr.idx);
 866                         memcpy(obr.buf + obr.idx, buf, n);
 867 
 868                         obr.idx += n;
 869 
 870                         if (obr.idx >= obr.len) {
 871                                 ret = sec_putbuf(fd, (unsigned char *)obr.buf,
 872                                         obr.idx);
 873                                 obr.idx = 0;
 874                                 if (ret < 0)
 875                                         return (ret);
 876                         }
 877                         len -= n;
 878                         nbytes += n;
 879                 }
 880         }
 881 
 882         return (nbytes);
 883 }
 884 
 885 /*
 886  * CCC
 887  *
 888  * Clear Command Channel.
 889  *
 890  * We will understand this command but not allow it in a secure
 891  * connection.  It is very dangerous to allow someone to degrade
 892  * the security of the command channel.  See RFC2228 for more info.
 893  */
 894 void
 895 ccc(void)
 896 {
 897         /*
 898          * Once we have negotiated security successfully,
 899          * do not allow the control channel to be downgraded.
 900          * It should be at least SAFE if not PRIVATE.
 901          */
 902         if (IS_GSSAUTH(cur_auth_type) &&
 903             (gss_info.authstate & GSS_ADAT_DONE) == GSS_ADAT_DONE)
 904                 reply(534, "Control channel may not be downgraded");
 905         else {
 906                 gss_info.ctrl_prot = PROT_C;
 907                 reply(200, "CCC ok");
 908         }
 909 }
 910 
 911 int
 912 sec_putc(int c, FILE *stream)
 913 {
 914         int ret = 0;
 915         /*
 916          * If we are NOT protecting the data
 917          * OR not using the GSSAPI authentication
 918          * OR GSSAPI data is not yet completed, send
 919          * plaintext.
 920          */
 921         if (gss_info.data_prot == PROT_C ||
 922             !IS_GSSAUTH(cur_auth_type) ||
 923             !(gss_info.authstate & GSS_ADAT_DONE))
 924                 return (putc(c, stream));
 925 
 926         /*
 927          * Add the latest byte to the current buffer
 928          */
 929         if (obr.idx < obr.len) {
 930                 obr.buf[obr.idx++] = (unsigned char)(c & 0xff);
 931         }
 932 
 933         if (obr.idx == obr.len) {
 934                 ret = sec_putbuf(fileno(stream), (uchar_t *)obr.buf, obr.idx);
 935                 if (ret >= 0)
 936                         ret = 0;
 937                 obr.idx = 0;
 938         }
 939 
 940         return ((ret == 0 ? c : ret));
 941 }
 942 
 943 int
 944 sec_fprintf(FILE *stream, char *fmt, ...)
 945 {
 946         int ret;
 947         va_list ap;
 948         va_start(ap, fmt);
 949 
 950         if (gss_info.data_prot == PROT_C ||
 951             !IS_GSSAUTH(cur_auth_type) ||
 952             !(gss_info.authstate & GSS_ADAT_DONE)) {
 953                 ret = vfprintf(stream, fmt, ap);
 954         } else {
 955                 (void) vsnprintf(obr.buf, obr.len, fmt, ap);
 956                 ret = sec_putbuf(fileno(stream), (unsigned char *)obr.buf,
 957                                 strlen(obr.buf));
 958         }
 959         va_end(ap);
 960         return (ret);
 961 }
 962 
 963 /*
 964  * sec_fflush
 965  *
 966  * If GSSAPI protection is configured, write out whatever remains
 967  * in the output buffer using the secure routines, otherwise
 968  * just flush the stream.
 969  */
 970 int
 971 sec_fflush(FILE *stream)
 972 {
 973         int ret = 0;
 974         if (gss_info.data_prot == PROT_C ||
 975             !IS_GSSAUTH(cur_auth_type) ||
 976             !(gss_info.authstate & GSS_ADAT_DONE)) {
 977                 fflush(stream);
 978                 return (0);
 979         }
 980         if (obr.idx > 0) {
 981                 ret = sec_putbuf(fileno(stream),
 982                                 (unsigned char *)obr.buf, obr.idx);
 983                 obr.idx = 0;
 984         }
 985 
 986         if (ret >= 0)
 987                 ret = sec_putbuf(fileno(stream), (unsigned char *)"", 0);
 988         /*
 989          * putbuf returns number of bytes or a negative value,
 990          * but fflush must return 0 or -1, so adjust the return
 991          * value so that a positive value is interpreted as success.
 992          */
 993         return (ret >= 0 ? 0 : ret);
 994 }
 995 
 996 /*
 997  * sec_getbytes
 998  *
 999  * Read and decrypt from the secure data channel.
1000  *
1001  * Return:
1002  *   > 0 == number of bytes available in gssbuf
1003  *   EOF == End of file.
1004  *   -2 == GSS error.
1005  *
1006  */
1007 static int
1008 sec_getbytes(int fd, char *buf, int nbytes)
1009 {
1010         /*
1011          * Only read from the network if our current buffer
1012          * is all used up.
1013          */
1014         if (ibr.idx >= ibr.clen) {
1015                 int kerror;
1016                 int conf_state;
1017                 unsigned int length;
1018                 gss_buffer_desc xmit_buf, msg_buf;
1019                 OM_uint32 maj_stat, min_stat;
1020 
1021                 if ((kerror = looping_read(fd, (char *)&length, 4)) != 4) {
1022                         reply(535, "Couldn't read PROT buffer length: %d/%s",
1023                             kerror,
1024                             (kerror == -1) ? strerror(errno) : "premature EOF");
1025                         return (-2);
1026                 }
1027 
1028                 if ((length = (unsigned int)ntohl(length)) > ibr.len) {
1029                         reply(535, "Length(%d) > PBSZ(%d)", length, ibr.len);
1030                         return (-2);
1031                 }
1032 
1033                 if (length > 0) {
1034                         if ((kerror = looping_read(fd, ibr.buf, length)) !=
1035                                 length) {
1036                                 reply(535, "Couldn't read %u byte PROT buf: %s",
1037                                         length, (kerror == -1) ?
1038                                         strerror(errno) : "premature EOF");
1039                                 return (-2);
1040                         }
1041 
1042                         xmit_buf.value = (char *)ibr.buf;
1043                         xmit_buf.length = length;
1044 
1045                         conf_state = (gss_info.data_prot == PROT_P);
1046 
1047                         /* decrypt/verify the message */
1048                         maj_stat = gss_unwrap(&min_stat, gss_info.context,
1049                                         &xmit_buf, &msg_buf, &conf_state, NULL);
1050                         if (maj_stat != GSS_S_COMPLETE) {
1051                                 reply_gss_error(535, maj_stat, min_stat,
1052                                         gss_info.mechoid,
1053                                         (gss_info.data_prot == PROT_P)?
1054                                         "failed unwrapping ENC message":
1055                                         "failed unwrapping MIC message");
1056                                 return (-2);
1057                         }
1058 
1059                         memcpy(ibr.buf, msg_buf.value, msg_buf.length);
1060                         ibr.clen = msg_buf.length;
1061                         ibr.idx = 0;
1062 
1063                         gss_release_buffer(&min_stat, &msg_buf);
1064                 } else {
1065                         ibr.idx = 0;
1066                         ibr.clen = 0;
1067                         return (EOF);
1068                 }
1069         }
1070 
1071         /*
1072          * If there are 'nbytes' of plain text available, use them, else
1073          * get whats available.
1074          */
1075         nbytes = (nbytes < (ibr.clen - ibr.idx) ? nbytes : ibr.clen - ibr.idx);
1076 
1077         memcpy(buf, ibr.buf + ibr.idx, nbytes);
1078         ibr.idx += nbytes;
1079 
1080         return ((nbytes == 0 ? EOF : nbytes));
1081 }
1082 
1083 /*
1084  * Get a buffer of 'maxlen' bytes from the client.
1085  * If we are using GSSAPI protection, use the secure
1086  * input buffer.
1087  */
1088 int
1089 sec_read(int fd, char *buf, int maxlen)
1090 {
1091         int nbytes = 0;
1092 
1093         if (gss_info.data_prot != PROT_C &&
1094             IS_GSSAUTH(cur_auth_type) &&
1095             (gss_info.authstate & GSS_ADAT_DONE)) {
1096                 /* Get as much data as possible */
1097                 nbytes = sec_getbytes(fd, buf, maxlen);
1098                 if (nbytes == EOF)
1099                         nbytes = 0;
1100         } else {
1101                 nbytes = read(fd, buf, maxlen);
1102         }
1103         return (nbytes);
1104 }
1105 
1106 /*
1107  * sec_getc
1108  *
1109  * Get a single character from the secure network buffer.
1110  */
1111 int
1112 sec_getc(FILE *stream)
1113 {
1114         int nbytes;
1115         unsigned char c;
1116 
1117         if (gss_info.data_prot != PROT_C &&
1118             IS_GSSAUTH(cur_auth_type) &&
1119             (gss_info.authstate & GSS_ADAT_DONE)) {
1120                 nbytes = sec_getbytes(fileno(stream), (char *)&c, 1);
1121                 if (nbytes > 0)
1122                         nbytes = (int)c;
1123                 return (nbytes);
1124         } else
1125                 return (getc(stream));
1126 }
1127 
1128 /*
1129  * sec_reply
1130  *
1131  * Securely encode a reply destined for the ftp client
1132  * depending on the GSSAPI settings.
1133  */
1134 int
1135 sec_reply(char *buf, int bufsiz, int n)
1136 {
1137         char  *out = NULL, *in = NULL;
1138         size_t inlen;
1139         gss_buffer_desc in_buf, out_buf;
1140         OM_uint32 maj_stat, min_stat;
1141         int conf_state, length, kerror;
1142         int ret = 0;
1143 
1144         if (debug)
1145                 syslog(LOG_DEBUG, "encoding %s", buf);
1146 
1147         in_buf.value = buf;
1148         in_buf.length = strlen(buf) + 1;
1149         maj_stat = gss_wrap(&min_stat, gss_info.context,
1150                             gss_info.ctrl_prot == PROT_P,
1151                             GSS_C_QOP_DEFAULT,
1152                             &in_buf, &conf_state,
1153                             &out_buf);
1154         if (maj_stat != GSS_S_COMPLETE) {
1155                 syslog(LOG_ERR, "gss_wrap %s did not complete",
1156                     (gss_info.ctrl_prot == PROT_P) ? "ENC": "MIC");
1157                 ret = -2;
1158                 gss_release_buffer(&min_stat, &out_buf);
1159                 goto end;
1160         } else if ((gss_info.ctrl_prot == PROT_P) && !conf_state) {
1161                 syslog(LOG_ERR, "gss_wrap did not encrypt message");
1162                 ret = -2;
1163                 gss_release_buffer(&min_stat, &out_buf);
1164                 goto end;
1165         } else {
1166                 out = (char *)malloc(out_buf.length);
1167                 if (out == NULL) {
1168                         syslog(LOG_ERR, "Memory error allocating buffer");
1169                         ret = -2;
1170                         gss_release_buffer(&min_stat, &out_buf);
1171                         goto end;
1172                 }
1173                 memcpy(out, out_buf.value, out_buf.length);
1174                 length = out_buf.length;
1175                 gss_release_buffer(&min_stat, &out_buf);
1176                 ret = 0;
1177         }
1178         /*
1179          * Base64 encode the reply.  encrypted "out" becomes
1180          * encoded "in" buffer.
1181          * Stick it all back in 'buf' for final output.
1182          */
1183         inlen = ENCODELEN(length);
1184         in = (char *)malloc(inlen);
1185         if (in == NULL) {
1186                 syslog(LOG_ERR, "Memory error allocating buffer");
1187                 ret = -2;
1188                 goto end;
1189         }
1190         if ((kerror = radix_encode((unsigned char *)out,
1191                                 (unsigned char *)in, inlen,
1192                                 &length, 0))) {
1193                 syslog(LOG_ERR, "Couldn't encode reply(%s)",
1194                     radix_error(kerror));
1195                 strncpy(buf, in, bufsiz-1);
1196                 buf[bufsiz - 1] = '\0';
1197         } else {
1198                 snprintf(buf, bufsiz, "%s%c%s",
1199                         gss_info.ctrl_prot == PROT_P ? "632" : "631",
1200                         n ? ' ' : '-', in);
1201         }
1202 end:
1203         if (in) free(in);
1204         if (out) free(out);
1205 
1206         return (ret);
1207 }
1208 
1209 /*
1210  * sec_decode_command
1211  *
1212  * If a command is received which is encoded(ENC, MIC, or CONF),
1213  * decode it here using GSSAPI.
1214  */
1215 char *
1216 sec_decode_command(char *cmd)
1217 {
1218         char *out = NULL, *cp;
1219         int len, mic, outlen;
1220         gss_buffer_desc xmit_buf, msg_buf;
1221         OM_uint32 maj_stat, min_stat;
1222         int conf_state;
1223         int kerror;
1224         char *cs;
1225         char *s = cmd;
1226 
1227         if ((cs = strpbrk(s, " \r\n")))
1228                 *cs++ = '\0';
1229         upper(s);
1230 
1231         if ((mic = strcmp(s, "ENC")) != 0 && strcmp(s, "MIC") &&
1232                 strcmp(s, "CONF")) {
1233                 reply(533, "All commands must be protected.");
1234                 syslog(LOG_ERR, "Unprotected command received %s", s);
1235                 *s = '\0';
1236                 return (s);
1237         }
1238 
1239         if ((cp = strpbrk(cs, " \r\n")))
1240                 *cp = '\0';
1241 
1242         outlen = DECODELEN(strlen(cs));
1243 
1244         out = (char *)malloc(outlen);
1245         if (out == NULL) {
1246                 reply(501, "Cannot decode response - not enough memory");
1247                 syslog(LOG_ERR, "Cannot decode response - not enough memory");
1248                 *s = '\0';
1249                 return (s);
1250         }
1251         len = strlen(cs);
1252         if ((kerror = radix_encode((unsigned char *)cs,
1253                                         (unsigned char *)out,
1254                                         outlen, &len, 1))) {
1255                 reply(501, "Can't base 64 decode argument to %s command(%s)",
1256                         mic ? "MIC" : "ENC", radix_error(kerror));
1257                 *s = '\0';
1258                 free(out);
1259                 return (s);
1260         }
1261 
1262         if (debug)
1263                 syslog(LOG_DEBUG, "getline got %d from %s <%s >\n",
1264                         len, cs, mic ? "MIC" : "ENC");
1265 
1266         xmit_buf.value = out;
1267         xmit_buf.length = len;
1268 
1269         /* decrypt the message */
1270         conf_state = !mic;
1271         maj_stat = gss_unwrap(&min_stat, gss_info.context, &xmit_buf,
1272                             &msg_buf, &conf_state, NULL);
1273         if (maj_stat == GSS_S_CONTINUE_NEEDED) {
1274                 if (debug) syslog(LOG_DEBUG, "%s-unwrap continued",
1275                                 mic ? "MIC" : "ENC");
1276                 reply(535, "%s-unwrap continued, oops", mic ? "MIC" : "ENC");
1277                 *s = 0;
1278                 free(out);
1279                 return (s);
1280         }
1281 
1282         free(out);
1283         if (maj_stat != GSS_S_COMPLETE) {
1284                 reply_gss_error(535, maj_stat, min_stat,
1285                                 gss_info.mechoid,
1286                                 mic ? "failed unwrapping MIC message":
1287                                 "failed unwrapping ENC message");
1288                 *s = 0;
1289                 return (s);
1290         }
1291 
1292         memcpy(s, msg_buf.value, msg_buf.length);
1293         strcpy(s + msg_buf.length-(s[msg_buf.length-1] ? 0 : 1), "\r\n");
1294         gss_release_buffer(&min_stat, &msg_buf);
1295 
1296         return (s);
1297 }
1298 
1299 #endif /* defined(USE_GSS) */