xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/gssutil.c (revision 530:bbb8de18489d)
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