xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/ftp/auth.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1985, 1989 Regents of the University of California.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include "ftp_var.h"
40 #include <sys/types.h>
41 #include <gssapi/gssapi.h>
42 #include <gssapi/gssapi_ext.h>
43 
44 int	auth_type;	/* Authentication succeeded?  If so, what type? */
45 
46 char	*radix_error(int);
47 static void get_inet_addr_info(struct sockaddr_in6 *, gss_buffer_t);
48 
49 static char *radixN =
50 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
51 static char radix_pad = '=';
52 
53 /*
54  * authenticate the user, if auth_type is AUTHTYPE_NONE
55  *
56  * Returns:	0 if there is no auth type
57  *		1 if success
58  * 		2 if failure
59  */
60 
61 gss_OID		mechoid;
62 gss_ctx_id_t	gcontext;	/* global gss security context */
63 static		const char *gss_trials[] = { "ftp", "host" };
64 /* the number of elements in gss_trials array */
65 static const	int n_gss_trials = sizeof (gss_trials)/sizeof (char *);
66 char		*reply_parse;
67 
68 int
do_auth(void)69 do_auth(void)
70 {
71 	int oldverbose = verbose;
72 	uchar_t *out_buf = NULL;
73 	size_t outlen;
74 	int i;
75 
76 	if (auth_type != AUTHTYPE_NONE)
77 	    return (1);		/* auth already succeeded */
78 
79 	/* Other auth types go here ... */
80 
81 	if (command("AUTH %s", "GSSAPI") == CONTINUE) {
82 	    OM_uint32 maj_stat, min_stat;
83 	    gss_name_t target_name;
84 	    gss_buffer_desc send_tok, recv_tok, *token_ptr;
85 	    gss_buffer_desc temp_buf;
86 	    char stbuf[FTPBUFSIZ];
87 	    int comcode, trial;
88 	    int req_flags;
89 	    struct gss_channel_bindings_struct chan;
90 
91 	    get_inet_addr_info(&myctladdr, &temp_buf);
92 	    chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32  */
93 	    chan.initiator_address.length =  temp_buf.length;
94 	    chan.initiator_address.value = malloc(temp_buf.length);
95 	    memcpy(chan.initiator_address.value, temp_buf.value,
96 		temp_buf.length);
97 
98 	    get_inet_addr_info(&remctladdr, &temp_buf);
99 	    chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
100 	    chan.acceptor_address.length = temp_buf.length;
101 	    chan.acceptor_address.value = malloc(temp_buf.length);
102 	    memcpy(chan.acceptor_address.value, temp_buf.value,
103 		temp_buf.length);
104 
105 	    chan.application_data.length = 0;
106 	    chan.application_data.value  = 0;
107 
108 	    if (verbose)
109 		(void) printf("GSSAPI accepted as authentication type\n");
110 
111 	    /* set the forward flag */
112 	    req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
113 
114 	    if (fflag)
115 		req_flags |= GSS_C_DELEG_FLAG;
116 
117 	    /* blob from gss-client */
118 	    for (trial = 0; trial < n_gss_trials; trial++) {
119 		/* ftp@hostname first, then host@hostname */
120 		/* the V5 GSSAPI binding canonicalizes this for us... */
121 		(void) snprintf(stbuf, FTPBUFSIZ, "%s@%s",
122 			gss_trials[trial], hostname);
123 		if (debug)
124 		    (void) fprintf(stderr,
125 			"Trying to authenticate to <%s>\n", stbuf);
126 
127 		send_tok.value = stbuf;
128 		send_tok.length = strlen(stbuf) + 1;
129 		maj_stat = gss_import_name(&min_stat, &send_tok,
130 			GSS_C_NT_HOSTBASED_SERVICE, &target_name);
131 
132 		if (maj_stat != GSS_S_COMPLETE) {
133 		    user_gss_error(maj_stat, min_stat, "parsing name");
134 		    (void) fprintf(stderr, "name parsed <%s>\n", stbuf);
135 		    continue;
136 		}
137 
138 		token_ptr = GSS_C_NO_BUFFER;
139 		gcontext = GSS_C_NO_CONTEXT; /* structure copy */
140 
141 		do {
142 		    if (debug)
143 			(void) fprintf(stderr,
144 				"calling gss_init_sec_context\n");
145 
146 		    if (mechstr && !mechoid &&
147 			__gss_mech_to_oid(mechstr, (gss_OID*)&mechoid) !=
148 			GSS_S_COMPLETE)
149 				(void) printf("do_auth: %s: not a valid "
150 					"security mechanism\n", mechstr);
151 
152 		    if (!mechoid)
153 			mechoid = GSS_C_NULL_OID;
154 
155 		    maj_stat = gss_init_sec_context(&min_stat,
156 				    GSS_C_NO_CREDENTIAL,
157 				    &gcontext,
158 				    target_name,
159 				    mechoid,
160 				    req_flags,
161 				    0,
162 				    &chan,	/* channel bindings */
163 				    token_ptr,
164 				    NULL,	/* ignore mech type */
165 				    &send_tok,
166 				    NULL,	/* ignore ret_flags */
167 				    NULL);	/* ignore time_rec */
168 
169 		    if (maj_stat != GSS_S_COMPLETE &&
170 			maj_stat != GSS_S_CONTINUE_NEEDED) {
171 
172 			/* return an error if this is NOT the ftp ticket */
173 			if (strcmp(gss_trials[trial], "ftp"))
174 				user_gss_error(maj_stat, min_stat,
175 					"initializing context");
176 
177 			(void) gss_release_name(&min_stat, &target_name);
178 			/* could just be that we missed on the service name */
179 			goto outer_loop;
180 
181 		    }
182 
183 		if (send_tok.length != 0) {
184 		    int len = send_tok.length;
185 		    reply_parse = "ADAT="; /* for command() later */
186 		    oldverbose = verbose;
187 		    verbose = (trial == n_gss_trials-1)?0:-1;
188 
189 		    outlen = ENCODELEN(send_tok.length);
190 		    out_buf = (uchar_t *)malloc(outlen);
191 		    if (out_buf == NULL) {
192 			(void) fprintf(stderr, "memory error allocating "
193 				"auth buffer\n");
194 			maj_stat = GSS_S_FAILURE;
195 			goto outer_loop;
196 		    }
197 		    auth_error = radix_encode(send_tok.value, out_buf,
198 			outlen, &len, 0);
199 
200 		    if (auth_error)  {
201 			(void) fprintf(stderr, "Base 64 encoding failed: %s\n",
202 				radix_error(auth_error));
203 		    } else if ((comcode = command("ADAT %s", out_buf))
204 			!= COMPLETE /* && comcode != 3 (335)*/) {
205 
206 			if (trial == n_gss_trials-1) {
207 			    (void) fprintf(stderr, "GSSAPI ADAT failed (%d)\n",
208 				comcode);
209 
210 			    /* force out of loop */
211 			    maj_stat = GSS_S_FAILURE;
212 			}
213 
214 			/*
215 			 * backoff to the v1 gssapi is still possible.
216 			 * Send a new AUTH command.  If that fails,
217 			 * terminate the loop
218 			 */
219 			if (command("AUTH %s", "GSSAPI") != CONTINUE) {
220 			    (void) fprintf(stderr,
221 				"GSSAPI ADAT failed, AUTH restart failed\n");
222 			    /* force out of loop */
223 			    maj_stat = GSS_S_FAILURE;
224 			}
225 
226 			goto outer_loop;
227 		    } else if (!reply_parse) {
228 			(void) fprintf(stderr,
229 			    "No authentication data received from server\n");
230 			if (maj_stat == GSS_S_COMPLETE) {
231 			    (void) fprintf(stderr,
232 				"...but no more was needed\n");
233 			    goto gss_complete_loop;
234 			} else {
235 			    user_gss_error(maj_stat, min_stat, "no reply.");
236 			    goto gss_complete_loop;
237 			}
238 		    } else if (auth_error = radix_encode((uchar_t *)
239 			reply_parse, out_buf, outlen, &i, 1)) {
240 			    (void) fprintf(stderr,
241 				"Base 64 decoding failed: %s\n",
242 				radix_error(auth_error));
243 		    } else {
244 			/* everything worked */
245 			token_ptr = &recv_tok;
246 			recv_tok.value = out_buf;
247 			recv_tok.length = i;
248 			continue;
249 		    } /* end if (auth_error) */
250 
251 /* get out of loop clean */
252 gss_complete_loop:
253 		    trial = n_gss_trials-1;
254 		    gss_release_buffer(&min_stat, &send_tok);
255 		    gss_release_name(&min_stat, &target_name);
256 		    goto outer_loop;
257 		} /* end if (send_tok.length != 0) */
258 
259 	    } while (maj_stat == GSS_S_CONTINUE_NEEDED);
260 
261 outer_loop:
262 	    if (maj_stat == GSS_S_COMPLETE)
263 		break;
264 
265 	    } /* end for loop */
266 
267 	    verbose = oldverbose;
268 	    if (out_buf != NULL)
269 		free(out_buf);
270 
271 	    if (maj_stat == GSS_S_COMPLETE) {
272 		(void) printf("GSSAPI authentication succeeded\n");
273 		reply_parse = NULL;
274 		auth_type = AUTHTYPE_GSSAPI;
275 		return (1);
276 	    } else {
277 		(void) fprintf(stderr, "GSSAPI authentication failed\n");
278 		reply_parse = NULL;
279 	    }
280 	} /* end if (command...) */
281 
282 	/* Other auth types go here ... */
283 
284 	return (0);
285 }
286 
287 /*
288  * Get the information for the channel structure.
289  */
290 void
get_inet_addr_info(struct sockaddr_in6 * in_ipaddr,gss_buffer_t in_buffer)291 get_inet_addr_info(struct sockaddr_in6 *in_ipaddr, gss_buffer_t in_buffer)
292 {
293 	size_t length;
294 	char *value;
295 
296 	if (in_ipaddr == NULL) {
297 		in_buffer->length = 0;
298 		in_buffer->value = NULL;
299 		return;
300 	}
301 
302 	/* get the initiator address.value and address.length */
303 
304 	if (in_ipaddr->sin6_family == AF_INET6) {
305 		struct in_addr in_ipv4addr;
306 		struct sockaddr_in6 *sin6 =
307 			(struct sockaddr_in6 *)in_ipaddr;
308 		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
309 			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
310 				&in_ipv4addr);
311 			in_buffer->length = length = sizeof (struct in_addr);
312 			in_buffer->value = value = malloc(length);
313 			memcpy(value, &in_ipv4addr, length);
314 		} else {
315 			in_buffer->length = length = sizeof (struct in6_addr);
316 			in_buffer->value = value = malloc(length);
317 			memcpy(value, &(sin6->sin6_addr.s6_addr),
318 				length);
319 		}
320 	} else {
321 		in_buffer->length = length = sizeof (struct in_addr);
322 		in_buffer->value = value = malloc(in_buffer->length);
323 		memcpy(value,
324 			&((struct sockaddr_in *)(in_ipaddr))->sin_addr,
325 			length);
326 	}
327 }
328 
329 int
radix_encode(uchar_t * inbuf,uchar_t * outbuf,size_t buflen,int * outlen,int decode)330 radix_encode(uchar_t *inbuf, uchar_t *outbuf, size_t buflen,
331 	int *outlen, int decode)
332 {
333 	int i, j, D;
334 	char *p;
335 	uchar_t c;
336 
337 	if (decode) {
338 		for (i = j = 0;
339 		    inbuf[i] && inbuf[i] != radix_pad && (j < buflen);
340 		    i++) {
341 		    if ((p = strchr(radixN, inbuf[i])) == NULL)
342 			return (1);
343 		    D = p - radixN;
344 		    switch (i&3) {
345 			case 0:
346 			    outbuf[j] = D<<2;
347 			    break;
348 			case 1:
349 			    outbuf[j++] |= D>>4;
350 			    outbuf[j] = (D&15)<<4;
351 			    break;
352 			case 2:
353 			    outbuf[j++] |= D>>2;
354 			    outbuf[j] = (D&3)<<6;
355 			    break;
356 			case 3:
357 			    outbuf[j++] |= D;
358 		    }
359 		}
360 		if (j == buflen && (inbuf[i] && inbuf[i] != radix_pad)) {
361 			return (4);
362 		}
363 		switch (i&3) {
364 			case 1: return (3);
365 			case 2: if (D&15)
366 					return (3);
367 				if (strcmp((char *)&inbuf[i], "=="))
368 					return (2);
369 				break;
370 			case 3: if (D&3)
371 					return (3);
372 				if (strcmp((char *)&inbuf[i], "="))
373 					return (2);
374 		}
375 		*outlen = j;
376 	} else {
377 		for (i = j = 0; i < *outlen && j < buflen; i++)
378 		    switch (i%3) {
379 			case 0:
380 			    outbuf[j++] = radixN[inbuf[i]>>2];
381 			    c = (inbuf[i]&3)<<4;
382 			    break;
383 			case 1:
384 			    outbuf[j++] = radixN[c|inbuf[i]>>4];
385 			    c = (inbuf[i]&15)<<2;
386 			    break;
387 			case 2:
388 			    outbuf[j++] = radixN[c|inbuf[i]>>6];
389 			    outbuf[j++] = radixN[inbuf[i]&63];
390 			    c = 0;
391 		    }
392 		if (j == buflen && i < *outlen) {
393 			return (4);
394 		}
395 		if (i%3)
396 			outbuf[j++] = radixN[c];
397 		switch (i%3) {
398 			case 1:
399 				outbuf[j++] = radix_pad;
400 				/* FALLTHROUGH */
401 			case 2:
402 				outbuf[j++] = radix_pad;
403 				break;
404 		}
405 		outbuf[*outlen = j] = '\0';
406 	}
407 	return (0);
408 }
409 
410 char *
radix_error(int e)411 radix_error(int e)
412 {
413 	switch (e) {
414 	    case 0:  return ("Success");
415 	    case 1:  return ("Bad character in encoding");
416 	    case 2:  return ("Encoding not properly padded");
417 	    case 3:  return ("Decoded # of bits not a multiple of 8");
418 	    case 4:  return ("Buffer size error");
419 	    default: return ("Unknown error");
420 	}
421 }
422