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