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