1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Shared routines for client and server for
31 * secure read(), write(), getc(), and putc().
32 * Only one security context, thus only work on one fd at a time!
33 */
34
35#include "ftp_var.h"
36#include <gssapi/gssapi.h>
37#include <arpa/ftp.h>
38#include <stdio.h>
39#include <string.h>
40#include <stdlib.h>
41#include <sys/types.h>
42#include <netinet/in.h>
43#include <errno.h>
44
45extern struct	sockaddr_in hisaddr;
46extern struct	sockaddr_in myaddr;
47extern int	dlevel;
48extern int	auth_type;
49extern uint_t	maxbuf; 	/* maximum output buffer size */
50extern uchar_t	*ucbuf;		/* cleartext buffer */
51static uint_t	nout;		/* number of chars in ucbuf */
52static uint_t	smaxbuf;	/* Internal saved value of maxbuf */
53static uint_t	smaxqueue;	/* Maximum allowed to queue before flush */
54
55extern gss_ctx_id_t gcontext;
56static int secure_putbuf(int, uchar_t *, uint_t);
57
58static int
59looping_write(int fd, const char *buf, int len)
60{
61	int cc, len2 = 0;
62
63	if (len == 0)
64		return (0);
65
66	do {
67		cc = write(fd, buf, len);
68		if (cc < 0) {
69			if (errno == EINTR)
70				continue;
71			return (cc);
72		} else if (cc == 0) {
73			return (len2);
74		} else {
75			buf += cc;
76			len2 += cc;
77			len -= cc;
78		}
79	} while (len > 0);
80	return (len2);
81}
82
83static int
84looping_read(int fd, char *buf, int len)
85{
86	int cc, len2 = 0;
87
88	do {
89		cc = read(fd, buf, len);
90		if (cc < 0) {
91			if (errno == EINTR)
92				continue;
93			return (cc);	/* errno is already set */
94		} else if (cc == 0) {
95			return (len2);
96		} else {
97			buf += cc;
98			len2 += cc;
99			len -= cc;
100		}
101	} while (len > 0);
102	return (len2);
103}
104
105#define	ERR	-2
106
107static void
108secure_error(char *fmt, ...)
109{
110	va_list ap;
111
112	va_start(ap, fmt);
113	vfprintf(stderr, fmt, ap);
114	va_end(ap);
115	putc('\n', stderr);
116}
117
118/*
119 * Given maxbuf as a buffer size, determine how much can we
120 * really transfer given the overhead of different algorithms
121 *
122 * Sets smaxbuf and smaxqueue
123 */
124
125static int
126secure_determine_constants(void)
127{
128	smaxbuf = maxbuf;
129	smaxqueue = maxbuf;
130
131	if (auth_type == AUTHTYPE_GSSAPI) {
132		OM_uint32 maj_stat, min_stat, mlen;
133		OM_uint32 msize = maxbuf;
134
135		maj_stat = gss_wrap_size_limit(&min_stat, gcontext,
136			(dlevel == PROT_P),
137			GSS_C_QOP_DEFAULT,
138			msize, &mlen);
139		if (maj_stat != GSS_S_COMPLETE) {
140			user_gss_error(maj_stat, min_stat,
141				"GSSAPI fudge determination");
142			/* Return error how? */
143			return (ERR);
144		}
145		smaxqueue = mlen;
146	}
147
148	return (0);
149}
150
151static uchar_t
152secure_putbyte(int fd, uchar_t c)
153{
154	int ret;
155
156	if ((smaxbuf == 0) || (smaxqueue == 0) || (smaxbuf != maxbuf)) {
157	    ret = secure_determine_constants();
158	    if (ret)
159		return (ret);
160	}
161	ucbuf[nout++] = c;
162	if (nout == smaxqueue) {
163		nout = 0;
164		ret = secure_putbuf(fd, ucbuf, smaxqueue);
165		return (ret ? ret :c);
166	}
167	return (c);
168}
169
170/*
171 * returns:
172 *	 0  on success
173 *	-1  on error (errno set)
174 *	-2  on security error
175 */
176int
177secure_flush(int fd)
178{
179	int ret;
180
181	if (dlevel == PROT_C)
182		return (0);
183	if (nout)
184		if (ret = secure_putbuf(fd, ucbuf, nout))
185			return (ret);
186	return (secure_putbuf(fd, (uchar_t *)"", nout = 0));
187}
188
189/*
190 * returns:
191 *	>= 0	on success
192 *	-1	on error
193 *	-2	on security error
194 */
195int
196secure_putc(int c, FILE *stream)
197{
198	if (dlevel == PROT_C)
199		return (putc(c, stream));
200	return (secure_putbyte(fileno(stream), (uchar_t)c));
201}
202
203/*
204 * returns:
205 *	nbyte on success
206 *	-1  on error (errno set)
207 *	-2  on security error
208 */
209ssize_t
210secure_write(int fd, const void *inbuf, size_t nbyte)
211{
212	uint_t i;
213	int c;
214	uchar_t *buf = (uchar_t *)inbuf;
215
216	if (dlevel == PROT_C)
217		return (write(fd, buf, nbyte));
218	for (i = 0; nbyte > 0; nbyte--)
219		if ((c = secure_putbyte(fd, buf[i++])) < 0)
220			return (c);
221	return (i);
222}
223
224/*
225 * returns:
226 *	 0  on success
227 *	-1  on error, errno set
228 *	-2  on security error
229 */
230static int secure_putbuf(int fd, uchar_t *buf, uint_t nbyte)
231{
232	static char *outbuf;		/* output ciphertext */
233	static uint_t bufsize;	/* size of outbuf */
234	int length;
235	uint_t net_len;
236
237	/* Other auth types go here ... */
238
239	if (auth_type == AUTHTYPE_GSSAPI) {
240		gss_buffer_desc in_buf, out_buf;
241		OM_uint32 maj_stat, min_stat;
242		int conf_state;
243
244		in_buf.value = buf;
245		in_buf.length = nbyte;
246		maj_stat = gss_seal(&min_stat, gcontext,
247				(dlevel == PROT_P), /* confidential */
248				GSS_C_QOP_DEFAULT,
249				&in_buf, &conf_state,
250				&out_buf);
251		if (maj_stat != GSS_S_COMPLETE) {
252			/*
253			 * generally need to deal
254			 * ie. should loop, but for now just fail
255			 */
256			user_gss_error(maj_stat, min_stat, dlevel == PROT_P?
257				"GSSAPI seal failed" : "GSSAPI sign failed");
258			return (ERR);
259		}
260
261		if (bufsize < out_buf.length) {
262			outbuf = outbuf ?
263				realloc(outbuf, (size_t)out_buf.length) :
264				malloc((size_t)out_buf.length);
265			if (outbuf)
266				bufsize = out_buf.length;
267			else {
268				bufsize = 0;
269				secure_error("%s (in malloc of PROT buffer)",
270					strerror(errno));
271				return (ERR);
272			}
273		}
274
275		memcpy(outbuf, out_buf.value, length = out_buf.length);
276		gss_release_buffer(&min_stat, &out_buf);
277	}
278	net_len = htonl((uint32_t)length);
279	if (looping_write(fd, (char *)&net_len, 4) == -1)
280		return (-1);
281	if (looping_write(fd, outbuf, length) != length)
282		return (-1);
283	return (0);
284}
285
286static int
287secure_getbyte(int fd)
288{
289	/* number of chars in ucbuf, pointer into ucbuf */
290	static uint_t nin, bufp;
291	int kerror;
292	uint_t length;
293
294	if (nin == 0) {
295		if ((kerror =
296			looping_read(fd, (char *)&length, sizeof (length)))
297			!= sizeof (length)) {
298			secure_error("Couldn't read PROT buffer length: %d/%s",
299				kerror, (kerror == -1) ? strerror(errno) :
300				"premature EOF");
301			return (ERR);
302		}
303		if ((length = ntohl((uint32_t)length)) > maxbuf) {
304			secure_error("Length (%d) of PROT buffer > PBSZ=%u",
305				length, maxbuf);
306			return (ERR);
307		}
308		if ((kerror = looping_read(fd, (char *)ucbuf, length))
309			!= length) {
310			secure_error("Couldn't read %u byte PROT buffer: %s",
311					length, kerror == -1 ?
312					strerror(errno) : "premature EOF");
313			return (ERR);
314		}
315		/* Other auth types go here ... */
316
317		if (auth_type == AUTHTYPE_GSSAPI) {
318			gss_buffer_desc xmit_buf, msg_buf;
319			OM_uint32 maj_stat, min_stat;
320			int conf_state;
321
322			xmit_buf.value = ucbuf;
323			xmit_buf.length = length;
324			conf_state = (dlevel == PROT_P);
325			/* decrypt/verify the message */
326			maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
327				&msg_buf, &conf_state, NULL);
328			if (maj_stat != GSS_S_COMPLETE) {
329				user_gss_error(maj_stat, min_stat,
330				    (dlevel == PROT_P)?
331				    "failed unsealing ENC message":
332				    "failed unsealing MIC message");
333				return (ERR);
334			}
335
336			memcpy(ucbuf, msg_buf.value,
337				nin = bufp = msg_buf.length);
338			gss_release_buffer(&min_stat, &msg_buf);
339		}
340		/* Other auth types go here ... */
341	}
342	return ((nin == 0) ? EOF : ucbuf[bufp - nin--]);
343}
344
345/*
346 * returns:
347 *	 0	on success
348 *	-1	on EOF
349 *	-2	on security error
350 */
351int
352secure_getc(FILE *stream)
353{
354	if (dlevel == PROT_C)
355		return (getc(stream));
356	return (secure_getbyte(fileno(stream)));
357}
358
359/*
360 * returns:
361 *	> 0	on success (n == # of bytes read)
362 *	 0	on EOF
363 *	-1	on error, errno set, only for PROT_C
364 *	-2	on security error (ERR = -2)
365 */
366ssize_t
367secure_read(int fd, void *inbuf, size_t nbyte)
368{
369	int c, i;
370	char *buf = (char *)inbuf;
371
372	if (dlevel == PROT_C)
373		return (read(fd, buf, nbyte));
374	if (goteof)
375		return (goteof = 0);
376
377	for (i = 0; nbyte > 0; nbyte--)
378		switch (c = secure_getbyte(fd)) {
379			case ERR:
380				return (c);
381			case EOF:
382				goteof = i ? 1 : 0;
383				return (i);
384			default:
385				buf[i++] = c;
386		}
387	return (i);
388}
389