10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate
60Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
70Sstevel@tonic-gate
80Sstevel@tonic-gate /*
90Sstevel@tonic-gate * gssutil.c
100Sstevel@tonic-gate *
110Sstevel@tonic-gate * Utility routines for providing security related services to
120Sstevel@tonic-gate * the FTP server. This code uses the GSSAPI (RFC 2743, 2744)
130Sstevel@tonic-gate * to provide a generic security layer to the application. The
140Sstevel@tonic-gate * security mechanism providing the actual security functions
150Sstevel@tonic-gate * is abstracted from the application itself. In the case of the FTP
160Sstevel@tonic-gate * server, the security mechanism is based on what the client chooses
170Sstevel@tonic-gate * to use when it makes the secure connection. If the client's
180Sstevel@tonic-gate * choice of GSS mechanism is not supported by the FTP server, the
190Sstevel@tonic-gate * connection may be rejected or fall back to standard Unix/PAM
200Sstevel@tonic-gate * authentication.
210Sstevel@tonic-gate *
220Sstevel@tonic-gate * This code is primarily intended to work with clients who choose
230Sstevel@tonic-gate * the Kerberos V5 GSSAPI mechanism as their security service.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #include "config.h"
270Sstevel@tonic-gate
280Sstevel@tonic-gate #if defined(USE_GSS)
290Sstevel@tonic-gate #include <stdio.h>
300Sstevel@tonic-gate #include <string.h>
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <ctype.h>
330Sstevel@tonic-gate #include <stdlib.h>
340Sstevel@tonic-gate #include <signal.h>
350Sstevel@tonic-gate #include <pwd.h>
360Sstevel@tonic-gate
370Sstevel@tonic-gate #include <netinet/in.h>
380Sstevel@tonic-gate #include <netinet/in_systm.h>
390Sstevel@tonic-gate #include <netinet/ip.h>
400Sstevel@tonic-gate
410Sstevel@tonic-gate #include <errno.h>
420Sstevel@tonic-gate #include <sys/param.h>
430Sstevel@tonic-gate #include <netdb.h>
440Sstevel@tonic-gate #ifdef HAVE_SYS_SYSLOG_H
450Sstevel@tonic-gate #include <sys/syslog.h>
460Sstevel@tonic-gate #endif
470Sstevel@tonic-gate
480Sstevel@tonic-gate /* CSTYLED */
490Sstevel@tonic-gate #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
500Sstevel@tonic-gate #include <syslog.h>
510Sstevel@tonic-gate #endif
520Sstevel@tonic-gate
530Sstevel@tonic-gate #ifdef HAVE_SYSINFO
540Sstevel@tonic-gate #include <sys/systeminfo.h>
550Sstevel@tonic-gate #endif
560Sstevel@tonic-gate
570Sstevel@tonic-gate #include <arpa/ftp.h>
580Sstevel@tonic-gate
590Sstevel@tonic-gate #include "gssutil.h"
600Sstevel@tonic-gate #include "proto.h"
610Sstevel@tonic-gate
620Sstevel@tonic-gate static char *gss_services[] = { "ftp", "host", 0 };
630Sstevel@tonic-gate
640Sstevel@tonic-gate gss_info_t gss_info = {
650Sstevel@tonic-gate /* context */ GSS_C_NO_CONTEXT,
660Sstevel@tonic-gate /* mechoid */ GSS_C_NULL_OID,
670Sstevel@tonic-gate /* client */ NULL,
680Sstevel@tonic-gate /* display_name */ NULL,
690Sstevel@tonic-gate /* data_prot */ PROT_C,
700Sstevel@tonic-gate /* ctrl_prot */ PROT_C,
710Sstevel@tonic-gate /* authstate */ GSS_AUTH_NONE,
720Sstevel@tonic-gate /* want_creds */ 0,
730Sstevel@tonic-gate /* have_creds */ 0,
740Sstevel@tonic-gate /* must_auth */ 0
750Sstevel@tonic-gate };
760Sstevel@tonic-gate
770Sstevel@tonic-gate
780Sstevel@tonic-gate extern char *cur_auth_type;
790Sstevel@tonic-gate extern struct SOCKSTORAGE his_addr;
800Sstevel@tonic-gate extern struct SOCKSTORAGE ctrl_addr;
810Sstevel@tonic-gate extern int debug;
820Sstevel@tonic-gate
830Sstevel@tonic-gate static char *radixN =
840Sstevel@tonic-gate "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
850Sstevel@tonic-gate
860Sstevel@tonic-gate static char pad = '=';
870Sstevel@tonic-gate
880Sstevel@tonic-gate #define DEF_GSSBUF_SIZE 2028
890Sstevel@tonic-gate #define DECODELEN(l) (((3 * (l)) / 4) + 4)
900Sstevel@tonic-gate #define ENCODELEN(l) (((4 * (l)) / 3) + 4)
910Sstevel@tonic-gate
920Sstevel@tonic-gate typedef struct {
930Sstevel@tonic-gate char *buf;
940Sstevel@tonic-gate size_t alloc_len;
950Sstevel@tonic-gate size_t len; /* max length of buffer */
960Sstevel@tonic-gate size_t idx; /* offset to beginning of read/write data */
970Sstevel@tonic-gate size_t clen; /* length of the remaining, decrypted data from client */
980Sstevel@tonic-gate }bufrec;
990Sstevel@tonic-gate
1000Sstevel@tonic-gate static bufrec obr = {NULL, 0, 0, 0, 0};
1010Sstevel@tonic-gate static bufrec ibr = {NULL, 0, 0, 0, 0};
1020Sstevel@tonic-gate
1030Sstevel@tonic-gate static int looping_write(int fd, const char *buf, size_t len);
1040Sstevel@tonic-gate static int looping_read(int fd, char *buf, size_t len);
1050Sstevel@tonic-gate static int radix_encode(unsigned char *inbuf, unsigned char *outbuf,
1060Sstevel@tonic-gate size_t len, int *outlen, int decode);
1070Sstevel@tonic-gate static char *radix_error(int e);
1080Sstevel@tonic-gate static void reply_gss_error(int code, OM_uint32 maj_stat,
1090Sstevel@tonic-gate OM_uint32 min_stat, gss_OID mechoid, char *s);
1100Sstevel@tonic-gate static void cleanup_bufrec(bufrec *brec);
1110Sstevel@tonic-gate static int alloc_bufrec(bufrec *brec, size_t newsz);
1120Sstevel@tonic-gate static int sec_putbuf(int fd, unsigned char *buf, int len);
1130Sstevel@tonic-gate static int sec_getbytes(int fd, char *buf, int nbytes);
1140Sstevel@tonic-gate
1150Sstevel@tonic-gate /*
1160Sstevel@tonic-gate * Provide a routine so that ftpd can know the max amount to read
1170Sstevel@tonic-gate */
1180Sstevel@tonic-gate size_t
gss_getinbufsz(void)1190Sstevel@tonic-gate gss_getinbufsz(void) {
1200Sstevel@tonic-gate return (ibr.len);
1210Sstevel@tonic-gate }
1220Sstevel@tonic-gate
1230Sstevel@tonic-gate /*
1240Sstevel@tonic-gate * gss_adjust_buflen
1250Sstevel@tonic-gate *
1260Sstevel@tonic-gate * Called when the protection method changes so we can adjust the
1270Sstevel@tonic-gate * "useable" length of our output buffer accordingly.
1280Sstevel@tonic-gate */
1290Sstevel@tonic-gate void
gss_adjust_buflen()1300Sstevel@tonic-gate gss_adjust_buflen()
1310Sstevel@tonic-gate {
1320Sstevel@tonic-gate OM_uint32 maj_stat, min_stat, mlen;
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate /*
1350Sstevel@tonic-gate * If we switched to CLEAR protection, we can use the entire buffer
1360Sstevel@tonic-gate */
1370Sstevel@tonic-gate if (gss_info.data_prot == PROT_C) {
1380Sstevel@tonic-gate obr.len = obr.alloc_len;
1390Sstevel@tonic-gate return;
1400Sstevel@tonic-gate }
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate /*
1430Sstevel@tonic-gate * Otherwise, determine the maximum size that will allow for
1440Sstevel@tonic-gate * the GSSAPI overhead to fit into the buffer size.
1450Sstevel@tonic-gate */
1460Sstevel@tonic-gate maj_stat = gss_wrap_size_limit(&min_stat, gss_info.context,
1470Sstevel@tonic-gate (gss_info.data_prot == PROT_P),
1480Sstevel@tonic-gate GSS_C_QOP_DEFAULT,
1490Sstevel@tonic-gate (OM_uint32)obr.alloc_len, &mlen);
1500Sstevel@tonic-gate if (maj_stat != GSS_S_COMPLETE) {
1510Sstevel@tonic-gate reply_gss_error(535, maj_stat, min_stat,
1520Sstevel@tonic-gate gss_info.mechoid,
1530Sstevel@tonic-gate "GSSAPI fudge determination");
1540Sstevel@tonic-gate return;
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate obr.len = mlen;
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate if (debug)
1590Sstevel@tonic-gate syslog(LOG_DEBUG, "GSSAPI alloc_len = %d len = %d",
1600Sstevel@tonic-gate obr.alloc_len, obr.len);
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate static int
looping_write(int fd,const char * buf,size_t len)1640Sstevel@tonic-gate looping_write(int fd, const char *buf, size_t len)
1650Sstevel@tonic-gate {
1660Sstevel@tonic-gate int cc;
1670Sstevel@tonic-gate register size_t wrlen = len;
1680Sstevel@tonic-gate
1690Sstevel@tonic-gate do {
1700Sstevel@tonic-gate cc = write(fd, buf, wrlen);
1710Sstevel@tonic-gate if (cc < 0) {
1720Sstevel@tonic-gate if (errno == EINTR)
1730Sstevel@tonic-gate continue;
1740Sstevel@tonic-gate return (cc);
1750Sstevel@tonic-gate } else {
1760Sstevel@tonic-gate buf += cc;
1770Sstevel@tonic-gate wrlen -= cc;
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate } while (wrlen > 0);
1800Sstevel@tonic-gate
1810Sstevel@tonic-gate return (len);
1820Sstevel@tonic-gate }
1830Sstevel@tonic-gate
1840Sstevel@tonic-gate static int
looping_read(int fd,char * buf,size_t len)1850Sstevel@tonic-gate looping_read(int fd, char *buf, size_t len)
1860Sstevel@tonic-gate {
1870Sstevel@tonic-gate int cc;
1880Sstevel@tonic-gate size_t len2 = 0;
1890Sstevel@tonic-gate
1900Sstevel@tonic-gate do {
1910Sstevel@tonic-gate cc = read(fd, buf, len);
1920Sstevel@tonic-gate if (cc < 0) {
1930Sstevel@tonic-gate if (errno == EINTR)
1940Sstevel@tonic-gate continue;
1950Sstevel@tonic-gate return (cc); /* errno is already set */
1960Sstevel@tonic-gate } else if (cc == 0) {
1970Sstevel@tonic-gate return (len2);
1980Sstevel@tonic-gate } else {
1990Sstevel@tonic-gate buf += cc;
2000Sstevel@tonic-gate len2 += cc;
2010Sstevel@tonic-gate len -= cc;
2020Sstevel@tonic-gate }
2030Sstevel@tonic-gate } while (len > 0);
2040Sstevel@tonic-gate return (len2);
2050Sstevel@tonic-gate }
2060Sstevel@tonic-gate
2070Sstevel@tonic-gate static int
radix_encode(unsigned char * inbuf,unsigned char * outbuf,size_t buflen,int * outlen,int decode)2080Sstevel@tonic-gate radix_encode(unsigned char *inbuf, unsigned char *outbuf,
2090Sstevel@tonic-gate size_t buflen, int *outlen, int decode)
2100Sstevel@tonic-gate {
2110Sstevel@tonic-gate register int i, j, D;
2120Sstevel@tonic-gate char *p;
2130Sstevel@tonic-gate unsigned char c;
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate if (decode) {
2160Sstevel@tonic-gate for (i = 0, j = 0; (j < buflen) &&
2170Sstevel@tonic-gate inbuf[i] && inbuf[i] != pad; i++) {
2180Sstevel@tonic-gate if ((p = strchr(radixN, inbuf[i])) == NULL)
2190Sstevel@tonic-gate return (1);
2200Sstevel@tonic-gate D = p - radixN;
2210Sstevel@tonic-gate switch (i&3) {
2220Sstevel@tonic-gate case 0:
2230Sstevel@tonic-gate outbuf[j] = D <<2;
2240Sstevel@tonic-gate break;
2250Sstevel@tonic-gate case 1:
2260Sstevel@tonic-gate outbuf[j++] |= D >>4;
2270Sstevel@tonic-gate outbuf[j] = (D&15)<<4;
2280Sstevel@tonic-gate break;
2290Sstevel@tonic-gate case 2:
2300Sstevel@tonic-gate outbuf[j++] |= D >>2;
2310Sstevel@tonic-gate outbuf[j] = (D&3)<<6;
2320Sstevel@tonic-gate break;
2330Sstevel@tonic-gate case 3:
2340Sstevel@tonic-gate outbuf[j++] |= D;
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate }
2370Sstevel@tonic-gate if (j == buflen && (inbuf[i] && inbuf[i] != pad)) {
2380Sstevel@tonic-gate /* Oops, we ran out of space in the output buffer */
2390Sstevel@tonic-gate return (4);
2400Sstevel@tonic-gate }
2410Sstevel@tonic-gate switch (i&3) {
2420Sstevel@tonic-gate case 1:
2430Sstevel@tonic-gate return (3);
2440Sstevel@tonic-gate case 2: if (D&15)
2450Sstevel@tonic-gate return (3);
2460Sstevel@tonic-gate if (strcmp((char *)&inbuf[i], "=="))
2470Sstevel@tonic-gate return (2);
2480Sstevel@tonic-gate break;
2490Sstevel@tonic-gate case 3: if (D&3)
2500Sstevel@tonic-gate return (3);
2510Sstevel@tonic-gate if (strcmp((char *)&inbuf[i], "="))
2520Sstevel@tonic-gate return (2);
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate *outlen = j;
2550Sstevel@tonic-gate } else {
2560Sstevel@tonic-gate for (i = 0, j = 0; i < *outlen && j < buflen; i++)
2570Sstevel@tonic-gate switch (i%3) {
2580Sstevel@tonic-gate case 0:
2590Sstevel@tonic-gate outbuf[j++] = radixN[inbuf[i]>>2];
2600Sstevel@tonic-gate c = (inbuf[i]&3)<<4;
2610Sstevel@tonic-gate break;
2620Sstevel@tonic-gate case 1:
2630Sstevel@tonic-gate outbuf[j++] = radixN[c|inbuf[i]>>4];
2640Sstevel@tonic-gate c = (inbuf[i]&15)<<2;
2650Sstevel@tonic-gate break;
2660Sstevel@tonic-gate case 2:
2670Sstevel@tonic-gate outbuf[j++] = radixN[c|inbuf[i]>>6];
2680Sstevel@tonic-gate outbuf[j++] = radixN[inbuf[i]&63];
2690Sstevel@tonic-gate c = 0;
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate if (j == buflen && i < *outlen) {
2720Sstevel@tonic-gate /* output buffer is not big enough */
2730Sstevel@tonic-gate return (4);
2740Sstevel@tonic-gate }
2750Sstevel@tonic-gate
2760Sstevel@tonic-gate if (i%3) outbuf[j++] = radixN[c];
2770Sstevel@tonic-gate switch (i%3) {
2780Sstevel@tonic-gate case 1: outbuf[j++] = pad;
2790Sstevel@tonic-gate case 2: outbuf[j++] = pad;
2800Sstevel@tonic-gate }
2810Sstevel@tonic-gate outbuf[*outlen = j] = '\0';
2820Sstevel@tonic-gate }
2830Sstevel@tonic-gate return (0);
2840Sstevel@tonic-gate }
2850Sstevel@tonic-gate
2860Sstevel@tonic-gate static char *
radix_error(int e)2870Sstevel@tonic-gate radix_error(int e)
2880Sstevel@tonic-gate {
2890Sstevel@tonic-gate switch (e) {
2900Sstevel@tonic-gate case 0: return ("Success");
2910Sstevel@tonic-gate case 1: return ("Bad character in encoding");
2920Sstevel@tonic-gate case 2: return ("Encoding not properly padded");
2930Sstevel@tonic-gate case 3: return ("Decoded # of bits not a multiple of 8");
2940Sstevel@tonic-gate case 4: return ("Buffer size error");
2950Sstevel@tonic-gate default: return ("Unknown error");
2960Sstevel@tonic-gate }
2970Sstevel@tonic-gate }
2980Sstevel@tonic-gate
2990Sstevel@tonic-gate static void
reply_gss_error(int code,OM_uint32 maj_stat,OM_uint32 min_stat,gss_OID mechoid,char * s)3000Sstevel@tonic-gate reply_gss_error(int code, OM_uint32 maj_stat,
3010Sstevel@tonic-gate OM_uint32 min_stat, gss_OID mechoid, char *s)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate /* a lot of work just to report the error */
3040Sstevel@tonic-gate OM_uint32 gmaj_stat, gmin_stat;
3050Sstevel@tonic-gate gss_buffer_desc msg;
3060Sstevel@tonic-gate int msg_ctx;
3070Sstevel@tonic-gate msg_ctx = 0;
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
3100Sstevel@tonic-gate GSS_C_GSS_CODE,
3110Sstevel@tonic-gate mechoid,
3120Sstevel@tonic-gate (OM_uint32 *)&msg_ctx, &msg);
3130Sstevel@tonic-gate if (gmaj_stat == GSS_S_COMPLETE) {
3140Sstevel@tonic-gate lreply(code, "GSSAPI error major: %s",
3150Sstevel@tonic-gate (char *)msg.value);
3160Sstevel@tonic-gate (void) gss_release_buffer(&gmin_stat, &msg);
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate gmaj_stat = gss_display_status(&gmin_stat, min_stat,
3200Sstevel@tonic-gate GSS_C_MECH_CODE,
3210Sstevel@tonic-gate mechoid,
3220Sstevel@tonic-gate (OM_uint32 *)&msg_ctx, &msg);
3230Sstevel@tonic-gate if (gmaj_stat == GSS_S_COMPLETE) {
3240Sstevel@tonic-gate lreply(code, "GSSAPI error minor: %s", (char *)msg.value);
3250Sstevel@tonic-gate (void) gss_release_buffer(&gmin_stat, &msg);
3260Sstevel@tonic-gate }
3270Sstevel@tonic-gate
3280Sstevel@tonic-gate reply(code, "GSSAPI error: %s", s);
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate
3320Sstevel@tonic-gate static void
log_status(char * msg,OM_uint32 status_code,int status_type)3330Sstevel@tonic-gate log_status(char *msg,
3340Sstevel@tonic-gate OM_uint32 status_code,
3350Sstevel@tonic-gate int status_type)
3360Sstevel@tonic-gate {
3370Sstevel@tonic-gate OM_uint32 message_context;
3380Sstevel@tonic-gate gss_buffer_desc status_string;
3390Sstevel@tonic-gate OM_uint32 maj_status;
3400Sstevel@tonic-gate OM_uint32 min_status;
3410Sstevel@tonic-gate
3420Sstevel@tonic-gate /* From RFC2744: */
3430Sstevel@tonic-gate message_context = 0;
3440Sstevel@tonic-gate
3450Sstevel@tonic-gate do {
3460Sstevel@tonic-gate maj_status = gss_display_status(
3470Sstevel@tonic-gate &min_status,
3480Sstevel@tonic-gate status_code,
3490Sstevel@tonic-gate status_type,
3500Sstevel@tonic-gate GSS_C_NO_OID,
3510Sstevel@tonic-gate &message_context,
3520Sstevel@tonic-gate &status_string);
3530Sstevel@tonic-gate
3540Sstevel@tonic-gate if (maj_status == GSS_S_COMPLETE) {
3550Sstevel@tonic-gate syslog(LOG_ERR,
3560Sstevel@tonic-gate "GSSAPI Error %s: %.*s\n",
3570Sstevel@tonic-gate msg ? msg : "<null>",
3580Sstevel@tonic-gate (int)status_string.length,
3590Sstevel@tonic-gate (char *)status_string.value);
3600Sstevel@tonic-gate
3610Sstevel@tonic-gate (void) gss_release_buffer(&min_status,
3620Sstevel@tonic-gate &status_string);
3630Sstevel@tonic-gate } else {
3640Sstevel@tonic-gate syslog(LOG_ERR,
3650Sstevel@tonic-gate "log_status internal error: gss_display_status failed");
3660Sstevel@tonic-gate return;
3670Sstevel@tonic-gate }
3680Sstevel@tonic-gate } while (message_context != 0);
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate }
3710Sstevel@tonic-gate
3720Sstevel@tonic-gate static void
log_gss_error(char * msg,OM_uint32 maj_stat,OM_uint32 min_stat)3730Sstevel@tonic-gate log_gss_error(char *msg,
3740Sstevel@tonic-gate OM_uint32 maj_stat,
3750Sstevel@tonic-gate OM_uint32 min_stat)
3760Sstevel@tonic-gate {
3770Sstevel@tonic-gate log_status(msg, maj_stat, GSS_C_GSS_CODE);
3780Sstevel@tonic-gate log_status(msg, min_stat, GSS_C_MECH_CODE);
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate
3810Sstevel@tonic-gate
3820Sstevel@tonic-gate static void
log_gss_info(int priority,char * luser,char * remprinc,gss_OID mechoid,char * s)3830Sstevel@tonic-gate log_gss_info(int priority,
3840Sstevel@tonic-gate char *luser,
3850Sstevel@tonic-gate char *remprinc,
3860Sstevel@tonic-gate gss_OID mechoid,
3870Sstevel@tonic-gate char *s)
3880Sstevel@tonic-gate {
3890Sstevel@tonic-gate const char *mechStr = __gss_oid_to_mech(mechoid);
3900Sstevel@tonic-gate
3910Sstevel@tonic-gate syslog(priority,
3920Sstevel@tonic-gate "%s: local user=`%s', remote princ=`%s', mech=%s",
3930Sstevel@tonic-gate s ? s : "<null>",
3940Sstevel@tonic-gate luser ? luser : "<null>",
3950Sstevel@tonic-gate remprinc ? remprinc : "<unknown>",
3960Sstevel@tonic-gate mechStr ? mechStr : "<unknown>");
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate /*
4000Sstevel@tonic-gate * gss_user
4010Sstevel@tonic-gate *
4020Sstevel@tonic-gate * Handle USER command after AUTH GSSAPI
4030Sstevel@tonic-gate *
4040Sstevel@tonic-gate * Check if the remote user can login to the local system w/out a passwd.
4050Sstevel@tonic-gate * Use the Solaris (private) interface (__gss_userok) if possible, else do
4060Sstevel@tonic-gate * a basic GSS-API compare.
4070Sstevel@tonic-gate *
4080Sstevel@tonic-gate * return 0 == BAD
4090Sstevel@tonic-gate * 1 == OK
4100Sstevel@tonic-gate */
4110Sstevel@tonic-gate int
gss_user(struct passwd * user_pw)4120Sstevel@tonic-gate gss_user(struct passwd *user_pw)
4130Sstevel@tonic-gate {
4140Sstevel@tonic-gate int retval = 0;
4150Sstevel@tonic-gate OM_uint32 status, minor;
4160Sstevel@tonic-gate
4170Sstevel@tonic-gate #ifdef SOLARIS_GSS_USEROK
4180Sstevel@tonic-gate
4190Sstevel@tonic-gate int user_ok = 0;
4200Sstevel@tonic-gate
4210Sstevel@tonic-gate if (debug)
4220Sstevel@tonic-gate log_gss_info(LOG_DEBUG,
4230Sstevel@tonic-gate user_pw->pw_name, gss_info.display_name,
4240Sstevel@tonic-gate gss_info.mechoid,
4250Sstevel@tonic-gate "gss_user: start (gss_userok)");
4260Sstevel@tonic-gate
4270Sstevel@tonic-gate /* gss_auth_rules(5) */
4280Sstevel@tonic-gate status = __gss_userok(&minor, gss_info.client,
4290Sstevel@tonic-gate user_pw->pw_name, &user_ok);
4300Sstevel@tonic-gate if (status == GSS_S_COMPLETE) {
4310Sstevel@tonic-gate if (user_ok) {
4320Sstevel@tonic-gate retval = 1; /* remote user is a-ok */
4330Sstevel@tonic-gate }
4340Sstevel@tonic-gate }
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate #else /* SOLARIS_GSS_USEROK */
4370Sstevel@tonic-gate
4380Sstevel@tonic-gate gss_name_t imported_name;
4390Sstevel@tonic-gate gss_name_t canon_name;
4400Sstevel@tonic-gate gss_buffer_desc gss_user;
4410Sstevel@tonic-gate OM_uint32 tmpMinor;
4420Sstevel@tonic-gate int match = 0;
4430Sstevel@tonic-gate
4440Sstevel@tonic-gate if (debug)
4450Sstevel@tonic-gate log_gss_info(LOG_DEBUG,
4460Sstevel@tonic-gate user_pw->pw_name, gss_info.display_name,
4470Sstevel@tonic-gate gss_info.mechoid, "gss_user: start");
4480Sstevel@tonic-gate
4490Sstevel@tonic-gate gss_user.value = user_pw->pw_name;
4500Sstevel@tonic-gate gss_user.length = strlen(gss_user.value);
4510Sstevel@tonic-gate
4520Sstevel@tonic-gate status = gss_import_name(&minor,
4530Sstevel@tonic-gate &gss_user,
4540Sstevel@tonic-gate GSS_C_NT_USER_NAME,
4550Sstevel@tonic-gate &imported_name);
4560Sstevel@tonic-gate if (status != GSS_S_COMPLETE) {
4570Sstevel@tonic-gate goto out;
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate
4600Sstevel@tonic-gate status = gss_canonicalize_name(&minor,
4610Sstevel@tonic-gate imported_name,
4620Sstevel@tonic-gate gss_info.mechoid,
4630Sstevel@tonic-gate &canon_name);
4640Sstevel@tonic-gate if (status != GSS_S_COMPLETE) {
4650Sstevel@tonic-gate (void) gss_release_name(&tmpMinor, &imported_name);
4660Sstevel@tonic-gate goto out;
4670Sstevel@tonic-gate }
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate status = gss_compare_name(&minor,
4700Sstevel@tonic-gate canon_name,
4710Sstevel@tonic-gate gss_info.client,
4720Sstevel@tonic-gate &match);
4730Sstevel@tonic-gate (void) gss_release_name(&tmpMinor, &canon_name);
4740Sstevel@tonic-gate (void) gss_release_name(&tmpMinor, &imported_name);
4750Sstevel@tonic-gate if (status == GSS_S_COMPLETE) {
4760Sstevel@tonic-gate if (match) {
4770Sstevel@tonic-gate retval = 1; /* remote user is a-ok */
4780Sstevel@tonic-gate }
4790Sstevel@tonic-gate }
4800Sstevel@tonic-gate
4810Sstevel@tonic-gate out:
4820Sstevel@tonic-gate
4830Sstevel@tonic-gate #endif /* SOLARIS_GSS_USEROK */
4840Sstevel@tonic-gate
4850Sstevel@tonic-gate if (status != GSS_S_COMPLETE) {
4860Sstevel@tonic-gate log_gss_info(LOG_ERR, user_pw->pw_name,
4870Sstevel@tonic-gate gss_info.display_name, gss_info.mechoid,
4880Sstevel@tonic-gate "gss_user failed");
4890Sstevel@tonic-gate log_gss_error("gss_user failed", status, minor);
4900Sstevel@tonic-gate }
4910Sstevel@tonic-gate
4920Sstevel@tonic-gate if (debug)
4930Sstevel@tonic-gate syslog(LOG_DEBUG, "gss_user: end: retval=%d", retval);
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate return (retval);
4960Sstevel@tonic-gate }
4970Sstevel@tonic-gate
4980Sstevel@tonic-gate
4990Sstevel@tonic-gate /*
5000Sstevel@tonic-gate * gss_adat
5010Sstevel@tonic-gate *
5020Sstevel@tonic-gate * Handle ADAT(Authentication Data) command data.
5030Sstevel@tonic-gate */
5040Sstevel@tonic-gate int
gss_adat(char * adatstr)5050Sstevel@tonic-gate gss_adat(char *adatstr)
5060Sstevel@tonic-gate {
5070Sstevel@tonic-gate int kerror, length;
5080Sstevel@tonic-gate int replied = 0;
5090Sstevel@tonic-gate int ret_flags;
5100Sstevel@tonic-gate gss_buffer_desc tok, out_tok;
5110Sstevel@tonic-gate gss_cred_id_t deleg_creds = NULL;
5120Sstevel@tonic-gate OM_uint32 accept_maj, accept_min;
5130Sstevel@tonic-gate OM_uint32 stat_maj, stat_min;
5140Sstevel@tonic-gate uchar_t *gout_buf;
5150Sstevel@tonic-gate size_t outlen;
5160Sstevel@tonic-gate
5170Sstevel@tonic-gate length = strlen(adatstr);
5180Sstevel@tonic-gate outlen = DECODELEN(length);
5190Sstevel@tonic-gate
5200Sstevel@tonic-gate gout_buf = (uchar_t *)malloc(outlen);
5210Sstevel@tonic-gate if (gout_buf == NULL) {
5220Sstevel@tonic-gate reply(501, "Couldn't decode ADAT, not enough memory");
5230Sstevel@tonic-gate syslog(LOG_ERR, "Couldn't decode ADAT, not enough memory");
5240Sstevel@tonic-gate return (0);
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate
5270Sstevel@tonic-gate if ((kerror = radix_encode((unsigned char *)adatstr,
5280Sstevel@tonic-gate (unsigned char *)gout_buf,
5290Sstevel@tonic-gate outlen, &length, 1))) {
5300Sstevel@tonic-gate reply(501, "Couldn't decode ADAT(%s)",
5310Sstevel@tonic-gate radix_error(kerror));
5320Sstevel@tonic-gate syslog(LOG_ERR, "Couldn't decode ADAT(%s)",
5330Sstevel@tonic-gate radix_error(kerror));
5340Sstevel@tonic-gate return (0);
5350Sstevel@tonic-gate }
5360Sstevel@tonic-gate tok.value = gout_buf;
5370Sstevel@tonic-gate tok.length = length;
5380Sstevel@tonic-gate
5390Sstevel@tonic-gate gss_info.context = GSS_C_NO_CONTEXT;
5400Sstevel@tonic-gate
5410Sstevel@tonic-gate /*
5420Sstevel@tonic-gate * Call accept_sec_context w/GSS_C_NO_CREDENTIAL to request
5430Sstevel@tonic-gate * default cred and to not limit the service name to one name
5440Sstevel@tonic-gate * but rather accept what the clnt requests if service
5450Sstevel@tonic-gate * princ/keys are available.
5460Sstevel@tonic-gate */
5470Sstevel@tonic-gate if (debug)
5480Sstevel@tonic-gate syslog(LOG_DEBUG,
5490Sstevel@tonic-gate "gss_adat: accept_sec_context will try default cred");
5500Sstevel@tonic-gate
5510Sstevel@tonic-gate out_tok.value = NULL;
5520Sstevel@tonic-gate out_tok.length = 0;
5530Sstevel@tonic-gate
5540Sstevel@tonic-gate accept_maj = gss_accept_sec_context(&accept_min,
5550Sstevel@tonic-gate &gss_info.context,
5560Sstevel@tonic-gate GSS_C_NO_CREDENTIAL,
5570Sstevel@tonic-gate &tok, /* ADAT data */
5580Sstevel@tonic-gate GSS_C_NO_CHANNEL_BINDINGS,
5590Sstevel@tonic-gate &gss_info.client,
5600Sstevel@tonic-gate &gss_info.mechoid,
5610Sstevel@tonic-gate &out_tok, /* output_token */
5620Sstevel@tonic-gate (unsigned int *)&ret_flags,
5630Sstevel@tonic-gate NULL, /* ignore time_rec */
5640Sstevel@tonic-gate NULL); /* delegated creds */
5650Sstevel@tonic-gate
5660Sstevel@tonic-gate
5670Sstevel@tonic-gate if (debug) {
5680Sstevel@tonic-gate if (accept_maj == GSS_S_COMPLETE)
5690Sstevel@tonic-gate syslog(LOG_DEBUG,
5700Sstevel@tonic-gate "gss_adat: accept_maj = GSS_S_COMPLETE");
5710Sstevel@tonic-gate else if (accept_maj == GSS_S_CONTINUE_NEEDED)
5720Sstevel@tonic-gate syslog(LOG_DEBUG,
5730Sstevel@tonic-gate "gss_adat: accept_maj = GSS_S_CONTINUE_NEEDED");
5740Sstevel@tonic-gate }
5750Sstevel@tonic-gate free(gout_buf);
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate if (accept_maj != GSS_S_COMPLETE &&
5780Sstevel@tonic-gate accept_maj != GSS_S_CONTINUE_NEEDED) {
5790Sstevel@tonic-gate reply_gss_error(535, accept_maj, accept_min,
5800Sstevel@tonic-gate GSS_C_NO_OID, "accepting context");
5810Sstevel@tonic-gate syslog(LOG_ERR, "failed accepting context");
5820Sstevel@tonic-gate if ((ret_flags & GSS_C_DELEG_FLAG) &&
5830Sstevel@tonic-gate deleg_creds != NULL)
5840Sstevel@tonic-gate (void) gss_release_cred(&stat_min,
5850Sstevel@tonic-gate &deleg_creds);
5860Sstevel@tonic-gate
5870Sstevel@tonic-gate (void) gss_release_buffer(&stat_min, &out_tok);
5880Sstevel@tonic-gate return (0);
5890Sstevel@tonic-gate }
5900Sstevel@tonic-gate
5910Sstevel@tonic-gate if (debug)
5920Sstevel@tonic-gate syslog(LOG_DEBUG, "gss_adat: out_tok.length=%d",
5930Sstevel@tonic-gate out_tok.length);
5940Sstevel@tonic-gate if (out_tok.length) {
5950Sstevel@tonic-gate size_t buflen = ENCODELEN(out_tok.length);
5960Sstevel@tonic-gate uchar_t *gbuf = (uchar_t *)malloc(buflen);
5970Sstevel@tonic-gate if (gbuf == NULL) {
5980Sstevel@tonic-gate reply(535, "Couldn't encode ADAT reply, "
5990Sstevel@tonic-gate "not enough memory.");
6000Sstevel@tonic-gate syslog(LOG_ERR, "Couldn't encode ADAT reply, "
6010Sstevel@tonic-gate "not enough memory.");
6020Sstevel@tonic-gate (void) gss_release_buffer(&stat_min, &out_tok);
6030Sstevel@tonic-gate return (0);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate if ((kerror = radix_encode(out_tok.value,
6060Sstevel@tonic-gate (unsigned char *)gbuf,
6070Sstevel@tonic-gate buflen, (int *)&out_tok.length,
6080Sstevel@tonic-gate 0))) {
6090Sstevel@tonic-gate reply(535, "Couldn't encode ADAT reply(%s)",
6100Sstevel@tonic-gate radix_error(kerror));
6110Sstevel@tonic-gate syslog(LOG_ERR, "couldn't encode ADAT reply");
6120Sstevel@tonic-gate if ((ret_flags & GSS_C_DELEG_FLAG) &&
6130Sstevel@tonic-gate deleg_creds != NULL)
6140Sstevel@tonic-gate (void) gss_release_cred(&stat_min,
6150Sstevel@tonic-gate &deleg_creds);
6160Sstevel@tonic-gate
6170Sstevel@tonic-gate (void) gss_release_buffer(&stat_min, &out_tok);
6180Sstevel@tonic-gate free(gbuf);
6190Sstevel@tonic-gate return (0);
6200Sstevel@tonic-gate }
6210Sstevel@tonic-gate
6220Sstevel@tonic-gate if (accept_maj == GSS_S_COMPLETE) {
6230Sstevel@tonic-gate reply(235, "ADAT=%s", gbuf);
6240Sstevel@tonic-gate replied = 1;
6250Sstevel@tonic-gate } else {
6260Sstevel@tonic-gate /*
6270Sstevel@tonic-gate * If the server accepts the security data, and
6280Sstevel@tonic-gate * requires additional data, it should respond
6290Sstevel@tonic-gate * with reply code 335.
6300Sstevel@tonic-gate */
6310Sstevel@tonic-gate reply(335, "ADAT=%s", gbuf);
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate free(gbuf);
6340Sstevel@tonic-gate (void) gss_release_buffer(&stat_min, &out_tok);
6350Sstevel@tonic-gate }
6360Sstevel@tonic-gate if (accept_maj == GSS_S_COMPLETE) {
6370Sstevel@tonic-gate gss_buffer_desc namebuf;
6380Sstevel@tonic-gate gss_OID out_oid;
6390Sstevel@tonic-gate
6400Sstevel@tonic-gate /* GSSAPI authentication succeeded */
6410Sstevel@tonic-gate gss_info.authstate = GSS_ADAT_DONE;
6420Sstevel@tonic-gate (void) alloc_bufrec(&obr, DEF_GSSBUF_SIZE);
6430Sstevel@tonic-gate (void) alloc_bufrec(&ibr, DEF_GSSBUF_SIZE);
6440Sstevel@tonic-gate /*
6450Sstevel@tonic-gate * RFC 2228 - "..., once a security data exchange completes
6460Sstevel@tonic-gate * successfully, if the security mechanism supports
6470Sstevel@tonic-gate * integrity, then integrity(via the MIC or ENC command,
6480Sstevel@tonic-gate * and 631 or 632 reply) must be used, ..."
6490Sstevel@tonic-gate */
6500Sstevel@tonic-gate gss_info.ctrl_prot = PROT_S;
6510Sstevel@tonic-gate
6520Sstevel@tonic-gate stat_maj = gss_display_name(&stat_min, gss_info.client,
6530Sstevel@tonic-gate &namebuf, &out_oid);
6540Sstevel@tonic-gate if (stat_maj != GSS_S_COMPLETE) {
6550Sstevel@tonic-gate /*
6560Sstevel@tonic-gate * RFC 2228 -
6570Sstevel@tonic-gate * "If the server rejects the security data(if
6580Sstevel@tonic-gate * a checksum fails, for instance), it should
6590Sstevel@tonic-gate * respond with reply code 535."
6600Sstevel@tonic-gate */
6610Sstevel@tonic-gate reply_gss_error(535, stat_maj, stat_min,
6620Sstevel@tonic-gate gss_info.mechoid,
6630Sstevel@tonic-gate "extracting GSSAPI identity name");
6640Sstevel@tonic-gate syslog(LOG_ERR, "gssapi error extracting identity");
6650Sstevel@tonic-gate if ((ret_flags & GSS_C_DELEG_FLAG) &&
6660Sstevel@tonic-gate deleg_creds != NULL)
6670Sstevel@tonic-gate (void) gss_release_cred(&stat_min,
6680Sstevel@tonic-gate &deleg_creds);
6690Sstevel@tonic-gate return (0);
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate gss_info.display_name = (char *)namebuf.value;
6720Sstevel@tonic-gate
6730Sstevel@tonic-gate if (ret_flags & GSS_C_DELEG_FLAG) {
6740Sstevel@tonic-gate gss_info.have_creds = 1;
6750Sstevel@tonic-gate if (deleg_creds != NULL)
6760Sstevel@tonic-gate (void) gss_release_cred(&stat_min,
6770Sstevel@tonic-gate &deleg_creds);
6780Sstevel@tonic-gate }
6790Sstevel@tonic-gate
6800Sstevel@tonic-gate /*
6810Sstevel@tonic-gate * If the server accepts the security data, but does
6820Sstevel@tonic-gate * not require any additional data(i.e., the security
6830Sstevel@tonic-gate * data exchange has completed successfully), it must
6840Sstevel@tonic-gate * respond with reply code 235.
6850Sstevel@tonic-gate */
6860Sstevel@tonic-gate if (!replied) {
6870Sstevel@tonic-gate if ((ret_flags & GSS_C_DELEG_FLAG) &&
6880Sstevel@tonic-gate !gss_info.have_creds)
6890Sstevel@tonic-gate reply(235,
6900Sstevel@tonic-gate "GSSAPI Authentication succeeded, but "
6910Sstevel@tonic-gate "could not accept forwarded credentials");
6920Sstevel@tonic-gate else
6930Sstevel@tonic-gate reply(235, "GSSAPI Authentication succeeded");
6940Sstevel@tonic-gate }
6950Sstevel@tonic-gate return (1);
6960Sstevel@tonic-gate } else if (accept_maj == GSS_S_CONTINUE_NEEDED) {
6970Sstevel@tonic-gate /*
6980Sstevel@tonic-gate * If the server accepts the security data, and
6990Sstevel@tonic-gate * requires additional data, it should respond with
7000Sstevel@tonic-gate * reply code 335.
7010Sstevel@tonic-gate */
7020Sstevel@tonic-gate reply(335, "more data needed");
7030Sstevel@tonic-gate if ((ret_flags & GSS_C_DELEG_FLAG) &&
7040Sstevel@tonic-gate deleg_creds != NULL)
7050Sstevel@tonic-gate (void) gss_release_cred(&stat_min, &deleg_creds);
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate
7080Sstevel@tonic-gate return (0);
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate
7110Sstevel@tonic-gate /*
7120Sstevel@tonic-gate * cleanup_bufrec
7130Sstevel@tonic-gate *
7140Sstevel@tonic-gate * cleanup the secure buffers
7150Sstevel@tonic-gate */
7160Sstevel@tonic-gate static void
cleanup_bufrec(bufrec * brec)7170Sstevel@tonic-gate cleanup_bufrec(bufrec *brec)
7180Sstevel@tonic-gate {
7190Sstevel@tonic-gate if (brec->buf)
7200Sstevel@tonic-gate free(brec->buf);
7210Sstevel@tonic-gate brec->len = 0;
7220Sstevel@tonic-gate brec->clen = 0;
7230Sstevel@tonic-gate brec->idx = 0;
7240Sstevel@tonic-gate }
7250Sstevel@tonic-gate
7260Sstevel@tonic-gate static int
alloc_bufrec(bufrec * brec,size_t newsz)7270Sstevel@tonic-gate alloc_bufrec(bufrec *brec, size_t newsz)
7280Sstevel@tonic-gate {
7290Sstevel@tonic-gate /*
7300Sstevel@tonic-gate * Try to allocate a buffer, if it fails,
7310Sstevel@tonic-gate * divide by 2 and try again.
7320Sstevel@tonic-gate */
7330Sstevel@tonic-gate cleanup_bufrec(brec);
7340Sstevel@tonic-gate
7350Sstevel@tonic-gate while (newsz > 0 && !(brec->buf = malloc(newsz))) {
7360Sstevel@tonic-gate syslog(LOG_ERR,
7370Sstevel@tonic-gate "malloc bufrec(%d bytes) failed, trying %d",
7380Sstevel@tonic-gate newsz >>= 1);
7390Sstevel@tonic-gate }
7400Sstevel@tonic-gate
7410Sstevel@tonic-gate if (brec->buf == NULL)
7420Sstevel@tonic-gate return (-1);
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate brec->alloc_len = newsz;
7450Sstevel@tonic-gate brec->len = newsz;
7460Sstevel@tonic-gate brec->clen = 0;
7470Sstevel@tonic-gate brec->idx = 0;
7480Sstevel@tonic-gate return (0);
7490Sstevel@tonic-gate }
7500Sstevel@tonic-gate
7510Sstevel@tonic-gate /*
7520Sstevel@tonic-gate * Handle PBSZ command data, return value to caller.
7530Sstevel@tonic-gate * RFC 2228 says this is a 32 bit int, so limit max value here.
7540Sstevel@tonic-gate */
7550Sstevel@tonic-gate unsigned int
gss_setpbsz(char * pbszstr)7560Sstevel@tonic-gate gss_setpbsz(char *pbszstr)
7570Sstevel@tonic-gate {
7580Sstevel@tonic-gate unsigned int newsz = 0;
7590Sstevel@tonic-gate char *endp;
760*530Shm123892 #define MAX_PBSZ 4294967295U
7610Sstevel@tonic-gate
7620Sstevel@tonic-gate errno = 0;
7630Sstevel@tonic-gate newsz = (unsigned int)strtol(pbszstr, &endp, 10);
7640Sstevel@tonic-gate if (errno != 0 || newsz > MAX_PBSZ || *endp != '\0') {
7650Sstevel@tonic-gate reply(501, "Bad value for PBSZ: %s", pbszstr);
7660Sstevel@tonic-gate return (0);
7670Sstevel@tonic-gate }
7680Sstevel@tonic-gate
7690Sstevel@tonic-gate if (newsz > ibr.len) {
7700Sstevel@tonic-gate if (alloc_bufrec(&obr, newsz) == -1) {
7710Sstevel@tonic-gate perror_reply(421, "Local resource failure: malloc");
7720Sstevel@tonic-gate dologout(1);
7730Sstevel@tonic-gate }
7740Sstevel@tonic-gate if (alloc_bufrec(&ibr, newsz) == -1) {
7750Sstevel@tonic-gate perror_reply(421, "Local resource failure: malloc");
7760Sstevel@tonic-gate dologout(1);
7770Sstevel@tonic-gate }
7780Sstevel@tonic-gate }
7790Sstevel@tonic-gate reply(200, "PBSZ =%lu", ibr.len);
7800Sstevel@tonic-gate
7810Sstevel@tonic-gate return (ibr.len);
7820Sstevel@tonic-gate }
7830Sstevel@tonic-gate
7840Sstevel@tonic-gate /*
7850Sstevel@tonic-gate * sec_putbuf
7860Sstevel@tonic-gate *
7870Sstevel@tonic-gate * Wrap the plaintext 'buf' data using gss_wrap and send
7880Sstevel@tonic-gate * it out.
7890Sstevel@tonic-gate *
7900Sstevel@tonic-gate * returns:
7910Sstevel@tonic-gate * bytes written (success)
7920Sstevel@tonic-gate * -1 on error(errno set)
7930Sstevel@tonic-gate * -2 on security error
7940Sstevel@tonic-gate */
7950Sstevel@tonic-gate static int
sec_putbuf(int fd,unsigned char * buf,int len)7960Sstevel@tonic-gate sec_putbuf(int fd, unsigned char *buf, int len)
7970Sstevel@tonic-gate {
7980Sstevel@tonic-gate unsigned long net_len;
7990Sstevel@tonic-gate int ret = 0;
8000Sstevel@tonic-gate gss_buffer_desc in_buf, out_buf;
8010Sstevel@tonic-gate OM_uint32 maj_stat, min_stat;
8020Sstevel@tonic-gate int conf_state;
8030Sstevel@tonic-gate
8040Sstevel@tonic-gate in_buf.value = buf;
8050Sstevel@tonic-gate in_buf.length = len;
8060Sstevel@tonic-gate maj_stat = gss_wrap(&min_stat, gss_info.context,
8070Sstevel@tonic-gate (gss_info.data_prot == PROT_P),
8080Sstevel@tonic-gate GSS_C_QOP_DEFAULT,
8090Sstevel@tonic-gate &in_buf, &conf_state,
8100Sstevel@tonic-gate &out_buf);
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate if (maj_stat != GSS_S_COMPLETE) {
8130Sstevel@tonic-gate reply_gss_error(535, maj_stat, min_stat,
8140Sstevel@tonic-gate gss_info.mechoid,
8150Sstevel@tonic-gate gss_info.data_prot == PROT_P ?
8160Sstevel@tonic-gate "GSSAPI wrap failed":
8170Sstevel@tonic-gate "GSSAPI sign failed");
8180Sstevel@tonic-gate return (-2);
8190Sstevel@tonic-gate }
8200Sstevel@tonic-gate
8210Sstevel@tonic-gate net_len = (unsigned long)htonl((unsigned long) out_buf.length);
8220Sstevel@tonic-gate
8230Sstevel@tonic-gate if ((ret = looping_write(fd, (const char *)&net_len, 4)) != 4) {
8240Sstevel@tonic-gate syslog(LOG_ERR, "Error writing net_len(%d): %m", net_len);
8250Sstevel@tonic-gate ret = -1;
8260Sstevel@tonic-gate goto putbuf_done;
8270Sstevel@tonic-gate }
8280Sstevel@tonic-gate
8290Sstevel@tonic-gate if ((ret = looping_write(fd, out_buf.value, out_buf.length)) !=
8300Sstevel@tonic-gate out_buf.length) {
8310Sstevel@tonic-gate syslog(LOG_ERR, "Error writing %d bytes: %m", out_buf.length);
8320Sstevel@tonic-gate ret = -1;
8330Sstevel@tonic-gate goto putbuf_done;
8340Sstevel@tonic-gate }
8350Sstevel@tonic-gate putbuf_done:
8360Sstevel@tonic-gate
8370Sstevel@tonic-gate gss_release_buffer(&min_stat, &out_buf);
8380Sstevel@tonic-gate return (ret);
8390Sstevel@tonic-gate }
8400Sstevel@tonic-gate
8410Sstevel@tonic-gate /*
8420Sstevel@tonic-gate * sec_write
8430Sstevel@tonic-gate *
8440Sstevel@tonic-gate * If GSSAPI security is established, encode the output
8450Sstevel@tonic-gate * and write it to the client. Else, just write it directly.
8460Sstevel@tonic-gate */
8470Sstevel@tonic-gate int
sec_write(int fd,char * buf,int len)8480Sstevel@tonic-gate sec_write(int fd, char *buf, int len)
8490Sstevel@tonic-gate {
8500Sstevel@tonic-gate int nbytes = 0;
8510Sstevel@tonic-gate if (gss_info.data_prot == PROT_C ||
8520Sstevel@tonic-gate !IS_GSSAUTH(cur_auth_type) ||
8530Sstevel@tonic-gate !(gss_info.authstate & GSS_ADAT_DONE))
8540Sstevel@tonic-gate nbytes = write(fd, buf, len);
8550Sstevel@tonic-gate else {
8560Sstevel@tonic-gate /*
8570Sstevel@tonic-gate * Fill up the buffer before actually encrypting
8580Sstevel@tonic-gate * and writing it out.
8590Sstevel@tonic-gate */
8600Sstevel@tonic-gate while ((obr.idx < obr.len) && (len > 0)) {
8610Sstevel@tonic-gate int n, ret;
8620Sstevel@tonic-gate
8630Sstevel@tonic-gate /* how many bytes can we fit into the buffer? */
8640Sstevel@tonic-gate n = (len < (obr.len - obr.idx) ? len :
8650Sstevel@tonic-gate obr.len - obr.idx);
8660Sstevel@tonic-gate memcpy(obr.buf + obr.idx, buf, n);
8670Sstevel@tonic-gate
8680Sstevel@tonic-gate obr.idx += n;
8690Sstevel@tonic-gate
8700Sstevel@tonic-gate if (obr.idx >= obr.len) {
8710Sstevel@tonic-gate ret = sec_putbuf(fd, (unsigned char *)obr.buf,
8720Sstevel@tonic-gate obr.idx);
8730Sstevel@tonic-gate obr.idx = 0;
8740Sstevel@tonic-gate if (ret < 0)
8750Sstevel@tonic-gate return (ret);
8760Sstevel@tonic-gate }
8770Sstevel@tonic-gate len -= n;
8780Sstevel@tonic-gate nbytes += n;
8790Sstevel@tonic-gate }
8800Sstevel@tonic-gate }
8810Sstevel@tonic-gate
8820Sstevel@tonic-gate return (nbytes);
8830Sstevel@tonic-gate }
8840Sstevel@tonic-gate
8850Sstevel@tonic-gate /*
8860Sstevel@tonic-gate * CCC
8870Sstevel@tonic-gate *
8880Sstevel@tonic-gate * Clear Command Channel.
8890Sstevel@tonic-gate *
8900Sstevel@tonic-gate * We will understand this command but not allow it in a secure
8910Sstevel@tonic-gate * connection. It is very dangerous to allow someone to degrade
8920Sstevel@tonic-gate * the security of the command channel. See RFC2228 for more info.
8930Sstevel@tonic-gate */
8940Sstevel@tonic-gate void
ccc(void)8950Sstevel@tonic-gate ccc(void)
8960Sstevel@tonic-gate {
8970Sstevel@tonic-gate /*
8980Sstevel@tonic-gate * Once we have negotiated security successfully,
8990Sstevel@tonic-gate * do not allow the control channel to be downgraded.
9000Sstevel@tonic-gate * It should be at least SAFE if not PRIVATE.
9010Sstevel@tonic-gate */
9020Sstevel@tonic-gate if (IS_GSSAUTH(cur_auth_type) &&
9030Sstevel@tonic-gate (gss_info.authstate & GSS_ADAT_DONE) == GSS_ADAT_DONE)
9040Sstevel@tonic-gate reply(534, "Control channel may not be downgraded");
9050Sstevel@tonic-gate else {
9060Sstevel@tonic-gate gss_info.ctrl_prot = PROT_C;
9070Sstevel@tonic-gate reply(200, "CCC ok");
9080Sstevel@tonic-gate }
9090Sstevel@tonic-gate }
9100Sstevel@tonic-gate
9110Sstevel@tonic-gate int
sec_putc(int c,FILE * stream)9120Sstevel@tonic-gate sec_putc(int c, FILE *stream)
9130Sstevel@tonic-gate {
9140Sstevel@tonic-gate int ret = 0;
9150Sstevel@tonic-gate /*
9160Sstevel@tonic-gate * If we are NOT protecting the data
9170Sstevel@tonic-gate * OR not using the GSSAPI authentication
9180Sstevel@tonic-gate * OR GSSAPI data is not yet completed, send
9190Sstevel@tonic-gate * plaintext.
9200Sstevel@tonic-gate */
9210Sstevel@tonic-gate if (gss_info.data_prot == PROT_C ||
9220Sstevel@tonic-gate !IS_GSSAUTH(cur_auth_type) ||
9230Sstevel@tonic-gate !(gss_info.authstate & GSS_ADAT_DONE))
9240Sstevel@tonic-gate return (putc(c, stream));
9250Sstevel@tonic-gate
9260Sstevel@tonic-gate /*
9270Sstevel@tonic-gate * Add the latest byte to the current buffer
9280Sstevel@tonic-gate */
9290Sstevel@tonic-gate if (obr.idx < obr.len) {
9300Sstevel@tonic-gate obr.buf[obr.idx++] = (unsigned char)(c & 0xff);
9310Sstevel@tonic-gate }
9320Sstevel@tonic-gate
9330Sstevel@tonic-gate if (obr.idx == obr.len) {
9340Sstevel@tonic-gate ret = sec_putbuf(fileno(stream), (uchar_t *)obr.buf, obr.idx);
9350Sstevel@tonic-gate if (ret >= 0)
9360Sstevel@tonic-gate ret = 0;
9370Sstevel@tonic-gate obr.idx = 0;
9380Sstevel@tonic-gate }
9390Sstevel@tonic-gate
9400Sstevel@tonic-gate return ((ret == 0 ? c : ret));
9410Sstevel@tonic-gate }
9420Sstevel@tonic-gate
9430Sstevel@tonic-gate int
sec_fprintf(FILE * stream,char * fmt,...)9440Sstevel@tonic-gate sec_fprintf(FILE *stream, char *fmt, ...)
9450Sstevel@tonic-gate {
9460Sstevel@tonic-gate int ret;
9470Sstevel@tonic-gate va_list ap;
9480Sstevel@tonic-gate va_start(ap, fmt);
9490Sstevel@tonic-gate
9500Sstevel@tonic-gate if (gss_info.data_prot == PROT_C ||
9510Sstevel@tonic-gate !IS_GSSAUTH(cur_auth_type) ||
9520Sstevel@tonic-gate !(gss_info.authstate & GSS_ADAT_DONE)) {
9530Sstevel@tonic-gate ret = vfprintf(stream, fmt, ap);
9540Sstevel@tonic-gate } else {
9550Sstevel@tonic-gate (void) vsnprintf(obr.buf, obr.len, fmt, ap);
9560Sstevel@tonic-gate ret = sec_putbuf(fileno(stream), (unsigned char *)obr.buf,
9570Sstevel@tonic-gate strlen(obr.buf));
9580Sstevel@tonic-gate }
9590Sstevel@tonic-gate va_end(ap);
9600Sstevel@tonic-gate return (ret);
9610Sstevel@tonic-gate }
9620Sstevel@tonic-gate
9630Sstevel@tonic-gate /*
9640Sstevel@tonic-gate * sec_fflush
9650Sstevel@tonic-gate *
9660Sstevel@tonic-gate * If GSSAPI protection is configured, write out whatever remains
9670Sstevel@tonic-gate * in the output buffer using the secure routines, otherwise
9680Sstevel@tonic-gate * just flush the stream.
9690Sstevel@tonic-gate */
9700Sstevel@tonic-gate int
sec_fflush(FILE * stream)9710Sstevel@tonic-gate sec_fflush(FILE *stream)
9720Sstevel@tonic-gate {
9730Sstevel@tonic-gate int ret = 0;
9740Sstevel@tonic-gate if (gss_info.data_prot == PROT_C ||
9750Sstevel@tonic-gate !IS_GSSAUTH(cur_auth_type) ||
9760Sstevel@tonic-gate !(gss_info.authstate & GSS_ADAT_DONE)) {
9770Sstevel@tonic-gate fflush(stream);
9780Sstevel@tonic-gate return (0);
9790Sstevel@tonic-gate }
9800Sstevel@tonic-gate if (obr.idx > 0) {
9810Sstevel@tonic-gate ret = sec_putbuf(fileno(stream),
9820Sstevel@tonic-gate (unsigned char *)obr.buf, obr.idx);
9830Sstevel@tonic-gate obr.idx = 0;
9840Sstevel@tonic-gate }
9850Sstevel@tonic-gate
9860Sstevel@tonic-gate if (ret >= 0)
9870Sstevel@tonic-gate ret = sec_putbuf(fileno(stream), (unsigned char *)"", 0);
9880Sstevel@tonic-gate /*
9890Sstevel@tonic-gate * putbuf returns number of bytes or a negative value,
9900Sstevel@tonic-gate * but fflush must return 0 or -1, so adjust the return
9910Sstevel@tonic-gate * value so that a positive value is interpreted as success.
9920Sstevel@tonic-gate */
9930Sstevel@tonic-gate return (ret >= 0 ? 0 : ret);
9940Sstevel@tonic-gate }
9950Sstevel@tonic-gate
9960Sstevel@tonic-gate /*
9970Sstevel@tonic-gate * sec_getbytes
9980Sstevel@tonic-gate *
9990Sstevel@tonic-gate * Read and decrypt from the secure data channel.
10000Sstevel@tonic-gate *
10010Sstevel@tonic-gate * Return:
10020Sstevel@tonic-gate * > 0 == number of bytes available in gssbuf
10030Sstevel@tonic-gate * EOF == End of file.
10040Sstevel@tonic-gate * -2 == GSS error.
10050Sstevel@tonic-gate *
10060Sstevel@tonic-gate */
10070Sstevel@tonic-gate static int
sec_getbytes(int fd,char * buf,int nbytes)10080Sstevel@tonic-gate sec_getbytes(int fd, char *buf, int nbytes)
10090Sstevel@tonic-gate {
10100Sstevel@tonic-gate /*
10110Sstevel@tonic-gate * Only read from the network if our current buffer
10120Sstevel@tonic-gate * is all used up.
10130Sstevel@tonic-gate */
10140Sstevel@tonic-gate if (ibr.idx >= ibr.clen) {
10150Sstevel@tonic-gate int kerror;
10160Sstevel@tonic-gate int conf_state;
10170Sstevel@tonic-gate unsigned int length;
10180Sstevel@tonic-gate gss_buffer_desc xmit_buf, msg_buf;
10190Sstevel@tonic-gate OM_uint32 maj_stat, min_stat;
10200Sstevel@tonic-gate
10210Sstevel@tonic-gate if ((kerror = looping_read(fd, (char *)&length, 4)) != 4) {
10220Sstevel@tonic-gate reply(535, "Couldn't read PROT buffer length: %d/%s",
10230Sstevel@tonic-gate kerror,
10240Sstevel@tonic-gate (kerror == -1) ? strerror(errno) : "premature EOF");
10250Sstevel@tonic-gate return (-2);
10260Sstevel@tonic-gate }
10270Sstevel@tonic-gate
10280Sstevel@tonic-gate if ((length = (unsigned int)ntohl(length)) > ibr.len) {
10290Sstevel@tonic-gate reply(535, "Length(%d) > PBSZ(%d)", length, ibr.len);
10300Sstevel@tonic-gate return (-2);
10310Sstevel@tonic-gate }
10320Sstevel@tonic-gate
10330Sstevel@tonic-gate if (length > 0) {
10340Sstevel@tonic-gate if ((kerror = looping_read(fd, ibr.buf, length)) !=
10350Sstevel@tonic-gate length) {
10360Sstevel@tonic-gate reply(535, "Couldn't read %u byte PROT buf: %s",
10370Sstevel@tonic-gate length, (kerror == -1) ?
10380Sstevel@tonic-gate strerror(errno) : "premature EOF");
10390Sstevel@tonic-gate return (-2);
10400Sstevel@tonic-gate }
10410Sstevel@tonic-gate
10420Sstevel@tonic-gate xmit_buf.value = (char *)ibr.buf;
10430Sstevel@tonic-gate xmit_buf.length = length;
10440Sstevel@tonic-gate
10450Sstevel@tonic-gate conf_state = (gss_info.data_prot == PROT_P);
10460Sstevel@tonic-gate
10470Sstevel@tonic-gate /* decrypt/verify the message */
10480Sstevel@tonic-gate maj_stat = gss_unwrap(&min_stat, gss_info.context,
10490Sstevel@tonic-gate &xmit_buf, &msg_buf, &conf_state, NULL);
10500Sstevel@tonic-gate if (maj_stat != GSS_S_COMPLETE) {
10510Sstevel@tonic-gate reply_gss_error(535, maj_stat, min_stat,
10520Sstevel@tonic-gate gss_info.mechoid,
10530Sstevel@tonic-gate (gss_info.data_prot == PROT_P)?
10540Sstevel@tonic-gate "failed unwrapping ENC message":
10550Sstevel@tonic-gate "failed unwrapping MIC message");
10560Sstevel@tonic-gate return (-2);
10570Sstevel@tonic-gate }
10580Sstevel@tonic-gate
10590Sstevel@tonic-gate memcpy(ibr.buf, msg_buf.value, msg_buf.length);
10600Sstevel@tonic-gate ibr.clen = msg_buf.length;
10610Sstevel@tonic-gate ibr.idx = 0;
10620Sstevel@tonic-gate
10630Sstevel@tonic-gate gss_release_buffer(&min_stat, &msg_buf);
10640Sstevel@tonic-gate } else {
10650Sstevel@tonic-gate ibr.idx = 0;
10660Sstevel@tonic-gate ibr.clen = 0;
10670Sstevel@tonic-gate return (EOF);
10680Sstevel@tonic-gate }
10690Sstevel@tonic-gate }
10700Sstevel@tonic-gate
10710Sstevel@tonic-gate /*
10720Sstevel@tonic-gate * If there are 'nbytes' of plain text available, use them, else
10730Sstevel@tonic-gate * get whats available.
10740Sstevel@tonic-gate */
10750Sstevel@tonic-gate nbytes = (nbytes < (ibr.clen - ibr.idx) ? nbytes : ibr.clen - ibr.idx);
10760Sstevel@tonic-gate
10770Sstevel@tonic-gate memcpy(buf, ibr.buf + ibr.idx, nbytes);
10780Sstevel@tonic-gate ibr.idx += nbytes;
10790Sstevel@tonic-gate
10800Sstevel@tonic-gate return ((nbytes == 0 ? EOF : nbytes));
10810Sstevel@tonic-gate }
10820Sstevel@tonic-gate
10830Sstevel@tonic-gate /*
10840Sstevel@tonic-gate * Get a buffer of 'maxlen' bytes from the client.
10850Sstevel@tonic-gate * If we are using GSSAPI protection, use the secure
10860Sstevel@tonic-gate * input buffer.
10870Sstevel@tonic-gate */
10880Sstevel@tonic-gate int
sec_read(int fd,char * buf,int maxlen)10890Sstevel@tonic-gate sec_read(int fd, char *buf, int maxlen)
10900Sstevel@tonic-gate {
10910Sstevel@tonic-gate int nbytes = 0;
10920Sstevel@tonic-gate
10930Sstevel@tonic-gate if (gss_info.data_prot != PROT_C &&
10940Sstevel@tonic-gate IS_GSSAUTH(cur_auth_type) &&
10950Sstevel@tonic-gate (gss_info.authstate & GSS_ADAT_DONE)) {
10960Sstevel@tonic-gate /* Get as much data as possible */
10970Sstevel@tonic-gate nbytes = sec_getbytes(fd, buf, maxlen);
10980Sstevel@tonic-gate if (nbytes == EOF)
10990Sstevel@tonic-gate nbytes = 0;
11000Sstevel@tonic-gate } else {
11010Sstevel@tonic-gate nbytes = read(fd, buf, maxlen);
11020Sstevel@tonic-gate }
11030Sstevel@tonic-gate return (nbytes);
11040Sstevel@tonic-gate }
11050Sstevel@tonic-gate
11060Sstevel@tonic-gate /*
11070Sstevel@tonic-gate * sec_getc
11080Sstevel@tonic-gate *
11090Sstevel@tonic-gate * Get a single character from the secure network buffer.
11100Sstevel@tonic-gate */
11110Sstevel@tonic-gate int
sec_getc(FILE * stream)11120Sstevel@tonic-gate sec_getc(FILE *stream)
11130Sstevel@tonic-gate {
11140Sstevel@tonic-gate int nbytes;
11150Sstevel@tonic-gate unsigned char c;
11160Sstevel@tonic-gate
11170Sstevel@tonic-gate if (gss_info.data_prot != PROT_C &&
11180Sstevel@tonic-gate IS_GSSAUTH(cur_auth_type) &&
11190Sstevel@tonic-gate (gss_info.authstate & GSS_ADAT_DONE)) {
11200Sstevel@tonic-gate nbytes = sec_getbytes(fileno(stream), (char *)&c, 1);
11210Sstevel@tonic-gate if (nbytes > 0)
11220Sstevel@tonic-gate nbytes = (int)c;
11230Sstevel@tonic-gate return (nbytes);
11240Sstevel@tonic-gate } else
11250Sstevel@tonic-gate return (getc(stream));
11260Sstevel@tonic-gate }
11270Sstevel@tonic-gate
11280Sstevel@tonic-gate /*
11290Sstevel@tonic-gate * sec_reply
11300Sstevel@tonic-gate *
11310Sstevel@tonic-gate * Securely encode a reply destined for the ftp client
11320Sstevel@tonic-gate * depending on the GSSAPI settings.
11330Sstevel@tonic-gate */
11340Sstevel@tonic-gate int
sec_reply(char * buf,int bufsiz,int n)11350Sstevel@tonic-gate sec_reply(char *buf, int bufsiz, int n)
11360Sstevel@tonic-gate {
11370Sstevel@tonic-gate char *out = NULL, *in = NULL;
11380Sstevel@tonic-gate size_t inlen;
11390Sstevel@tonic-gate gss_buffer_desc in_buf, out_buf;
11400Sstevel@tonic-gate OM_uint32 maj_stat, min_stat;
11410Sstevel@tonic-gate int conf_state, length, kerror;
11420Sstevel@tonic-gate int ret = 0;
11430Sstevel@tonic-gate
11440Sstevel@tonic-gate if (debug)
11450Sstevel@tonic-gate syslog(LOG_DEBUG, "encoding %s", buf);
11460Sstevel@tonic-gate
11470Sstevel@tonic-gate in_buf.value = buf;
11480Sstevel@tonic-gate in_buf.length = strlen(buf) + 1;
11490Sstevel@tonic-gate maj_stat = gss_wrap(&min_stat, gss_info.context,
11500Sstevel@tonic-gate gss_info.ctrl_prot == PROT_P,
11510Sstevel@tonic-gate GSS_C_QOP_DEFAULT,
11520Sstevel@tonic-gate &in_buf, &conf_state,
11530Sstevel@tonic-gate &out_buf);
11540Sstevel@tonic-gate if (maj_stat != GSS_S_COMPLETE) {
11550Sstevel@tonic-gate syslog(LOG_ERR, "gss_wrap %s did not complete",
11560Sstevel@tonic-gate (gss_info.ctrl_prot == PROT_P) ? "ENC": "MIC");
11570Sstevel@tonic-gate ret = -2;
11580Sstevel@tonic-gate gss_release_buffer(&min_stat, &out_buf);
11590Sstevel@tonic-gate goto end;
11600Sstevel@tonic-gate } else if ((gss_info.ctrl_prot == PROT_P) && !conf_state) {
11610Sstevel@tonic-gate syslog(LOG_ERR, "gss_wrap did not encrypt message");
11620Sstevel@tonic-gate ret = -2;
11630Sstevel@tonic-gate gss_release_buffer(&min_stat, &out_buf);
11640Sstevel@tonic-gate goto end;
11650Sstevel@tonic-gate } else {
11660Sstevel@tonic-gate out = (char *)malloc(out_buf.length);
11670Sstevel@tonic-gate if (out == NULL) {
11680Sstevel@tonic-gate syslog(LOG_ERR, "Memory error allocating buffer");
11690Sstevel@tonic-gate ret = -2;
11700Sstevel@tonic-gate gss_release_buffer(&min_stat, &out_buf);
11710Sstevel@tonic-gate goto end;
11720Sstevel@tonic-gate }
11730Sstevel@tonic-gate memcpy(out, out_buf.value, out_buf.length);
11740Sstevel@tonic-gate length = out_buf.length;
11750Sstevel@tonic-gate gss_release_buffer(&min_stat, &out_buf);
11760Sstevel@tonic-gate ret = 0;
11770Sstevel@tonic-gate }
11780Sstevel@tonic-gate /*
11790Sstevel@tonic-gate * Base64 encode the reply. encrypted "out" becomes
11800Sstevel@tonic-gate * encoded "in" buffer.
11810Sstevel@tonic-gate * Stick it all back in 'buf' for final output.
11820Sstevel@tonic-gate */
11830Sstevel@tonic-gate inlen = ENCODELEN(length);
11840Sstevel@tonic-gate in = (char *)malloc(inlen);
11850Sstevel@tonic-gate if (in == NULL) {
11860Sstevel@tonic-gate syslog(LOG_ERR, "Memory error allocating buffer");
11870Sstevel@tonic-gate ret = -2;
11880Sstevel@tonic-gate goto end;
11890Sstevel@tonic-gate }
11900Sstevel@tonic-gate if ((kerror = radix_encode((unsigned char *)out,
11910Sstevel@tonic-gate (unsigned char *)in, inlen,
11920Sstevel@tonic-gate &length, 0))) {
11930Sstevel@tonic-gate syslog(LOG_ERR, "Couldn't encode reply(%s)",
11940Sstevel@tonic-gate radix_error(kerror));
11950Sstevel@tonic-gate strncpy(buf, in, bufsiz-1);
11960Sstevel@tonic-gate buf[bufsiz - 1] = '\0';
11970Sstevel@tonic-gate } else {
11980Sstevel@tonic-gate snprintf(buf, bufsiz, "%s%c%s",
11990Sstevel@tonic-gate gss_info.ctrl_prot == PROT_P ? "632" : "631",
12000Sstevel@tonic-gate n ? ' ' : '-', in);
12010Sstevel@tonic-gate }
12020Sstevel@tonic-gate end:
12030Sstevel@tonic-gate if (in) free(in);
12040Sstevel@tonic-gate if (out) free(out);
12050Sstevel@tonic-gate
12060Sstevel@tonic-gate return (ret);
12070Sstevel@tonic-gate }
12080Sstevel@tonic-gate
12090Sstevel@tonic-gate /*
12100Sstevel@tonic-gate * sec_decode_command
12110Sstevel@tonic-gate *
12120Sstevel@tonic-gate * If a command is received which is encoded(ENC, MIC, or CONF),
12130Sstevel@tonic-gate * decode it here using GSSAPI.
12140Sstevel@tonic-gate */
12150Sstevel@tonic-gate char *
sec_decode_command(char * cmd)12160Sstevel@tonic-gate sec_decode_command(char *cmd)
12170Sstevel@tonic-gate {
12180Sstevel@tonic-gate char *out = NULL, *cp;
12190Sstevel@tonic-gate int len, mic, outlen;
12200Sstevel@tonic-gate gss_buffer_desc xmit_buf, msg_buf;
12210Sstevel@tonic-gate OM_uint32 maj_stat, min_stat;
12220Sstevel@tonic-gate int conf_state;
12230Sstevel@tonic-gate int kerror;
12240Sstevel@tonic-gate char *cs;
12250Sstevel@tonic-gate char *s = cmd;
12260Sstevel@tonic-gate
12270Sstevel@tonic-gate if ((cs = strpbrk(s, " \r\n")))
12280Sstevel@tonic-gate *cs++ = '\0';
12290Sstevel@tonic-gate upper(s);
12300Sstevel@tonic-gate
12310Sstevel@tonic-gate if ((mic = strcmp(s, "ENC")) != 0 && strcmp(s, "MIC") &&
12320Sstevel@tonic-gate strcmp(s, "CONF")) {
12330Sstevel@tonic-gate reply(533, "All commands must be protected.");
12340Sstevel@tonic-gate syslog(LOG_ERR, "Unprotected command received %s", s);
12350Sstevel@tonic-gate *s = '\0';
12360Sstevel@tonic-gate return (s);
12370Sstevel@tonic-gate }
12380Sstevel@tonic-gate
12390Sstevel@tonic-gate if ((cp = strpbrk(cs, " \r\n")))
12400Sstevel@tonic-gate *cp = '\0';
12410Sstevel@tonic-gate
12420Sstevel@tonic-gate outlen = DECODELEN(strlen(cs));
12430Sstevel@tonic-gate
12440Sstevel@tonic-gate out = (char *)malloc(outlen);
12450Sstevel@tonic-gate if (out == NULL) {
12460Sstevel@tonic-gate reply(501, "Cannot decode response - not enough memory");
12470Sstevel@tonic-gate syslog(LOG_ERR, "Cannot decode response - not enough memory");
12480Sstevel@tonic-gate *s = '\0';
12490Sstevel@tonic-gate return (s);
12500Sstevel@tonic-gate }
12510Sstevel@tonic-gate len = strlen(cs);
12520Sstevel@tonic-gate if ((kerror = radix_encode((unsigned char *)cs,
12530Sstevel@tonic-gate (unsigned char *)out,
12540Sstevel@tonic-gate outlen, &len, 1))) {
12550Sstevel@tonic-gate reply(501, "Can't base 64 decode argument to %s command(%s)",
12560Sstevel@tonic-gate mic ? "MIC" : "ENC", radix_error(kerror));
12570Sstevel@tonic-gate *s = '\0';
12580Sstevel@tonic-gate free(out);
12590Sstevel@tonic-gate return (s);
12600Sstevel@tonic-gate }
12610Sstevel@tonic-gate
12620Sstevel@tonic-gate if (debug)
12630Sstevel@tonic-gate syslog(LOG_DEBUG, "getline got %d from %s <%s >\n",
12640Sstevel@tonic-gate len, cs, mic ? "MIC" : "ENC");
12650Sstevel@tonic-gate
12660Sstevel@tonic-gate xmit_buf.value = out;
12670Sstevel@tonic-gate xmit_buf.length = len;
12680Sstevel@tonic-gate
12690Sstevel@tonic-gate /* decrypt the message */
12700Sstevel@tonic-gate conf_state = !mic;
12710Sstevel@tonic-gate maj_stat = gss_unwrap(&min_stat, gss_info.context, &xmit_buf,
12720Sstevel@tonic-gate &msg_buf, &conf_state, NULL);
12730Sstevel@tonic-gate if (maj_stat == GSS_S_CONTINUE_NEEDED) {
12740Sstevel@tonic-gate if (debug) syslog(LOG_DEBUG, "%s-unwrap continued",
12750Sstevel@tonic-gate mic ? "MIC" : "ENC");
12760Sstevel@tonic-gate reply(535, "%s-unwrap continued, oops", mic ? "MIC" : "ENC");
12770Sstevel@tonic-gate *s = 0;
12780Sstevel@tonic-gate free(out);
12790Sstevel@tonic-gate return (s);
12800Sstevel@tonic-gate }
12810Sstevel@tonic-gate
12820Sstevel@tonic-gate free(out);
12830Sstevel@tonic-gate if (maj_stat != GSS_S_COMPLETE) {
12840Sstevel@tonic-gate reply_gss_error(535, maj_stat, min_stat,
12850Sstevel@tonic-gate gss_info.mechoid,
12860Sstevel@tonic-gate mic ? "failed unwrapping MIC message":
12870Sstevel@tonic-gate "failed unwrapping ENC message");
12880Sstevel@tonic-gate *s = 0;
12890Sstevel@tonic-gate return (s);
12900Sstevel@tonic-gate }
12910Sstevel@tonic-gate
12920Sstevel@tonic-gate memcpy(s, msg_buf.value, msg_buf.length);
12930Sstevel@tonic-gate strcpy(s + msg_buf.length-(s[msg_buf.length-1] ? 0 : 1), "\r\n");
12940Sstevel@tonic-gate gss_release_buffer(&min_stat, &msg_buf);
12950Sstevel@tonic-gate
12960Sstevel@tonic-gate return (s);
12970Sstevel@tonic-gate }
12980Sstevel@tonic-gate
12990Sstevel@tonic-gate #endif /* defined(USE_GSS) */
1300