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