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