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