1/* saslutil.c
2 * Rob Siemborski
3 * Tim Martin
4 * $Id: saslutil.c,v 1.6 2005/05/17 21:56:43 snsimon Exp $
5 */
6/*
7 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in
18 *    the documentation and/or other materials provided with the
19 *    distribution.
20 *
21 * 3. The name "Carnegie Mellon University" must not be used to
22 *    endorse or promote products derived from this software without
23 *    prior written permission. For permission or any other legal
24 *    details, please contact
25 *      Office of Technology Transfer
26 *      Carnegie Mellon University
27 *      5000 Forbes Avenue
28 *      Pittsburgh, PA  15213-3890
29 *      (412) 268-4387, fax: (412) 268-7395
30 *      tech-transfer@andrew.cmu.edu
31 *
32 * 4. Redistributions of any form whatsoever must retain the following
33 *    acknowledgment:
34 *    "This product includes software developed by Computing Services
35 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36 *
37 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
38 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
40 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
42 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
43 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44 */
45
46#include <config.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <ctype.h>
51#include <sys/types.h>
52#include <sys/stat.h>
53#include <fcntl.h>
54#include <errno.h>
55#ifdef HAVE_UNISTD_H
56#include <unistd.h>
57#endif
58#ifdef HAVE_TIME_H
59#include <time.h>
60#endif
61#include "saslint.h"
62#include <saslutil.h>
63
64/*  Contains:
65 *
66 * sasl_decode64
67 * sasl_encode64
68 * sasl_mkchal
69 * sasl_utf8verify
70 * sasl_randcreate
71 * sasl_randfree
72 * sasl_randseed
73 * sasl_rand
74 * sasl_churn
75*/
76
77char *encode_table;
78char *decode_table;
79
80#define RPOOL_SIZE 3
81struct sasl_rand_s {
82    unsigned short pool[RPOOL_SIZE];
83    /* since the init time might be really bad let's make this lazy */
84    int initialized;
85};
86
87#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
88
89static char basis_64[] =
90   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";
91
92static char index_64[128] = {
93    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
94    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
95    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
96    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
97    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
98    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
99    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
100    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
101};
102
103/* base64 encode
104 *  in      -- input data
105 *  inlen   -- input data length
106 *  out     -- output buffer (will be NUL terminated)
107 *  outmax  -- max size of output buffer
108 * result:
109 *  outlen  -- gets actual length of output buffer (optional)
110 *
111 * Returns SASL_OK on success, SASL_BUFOVER if result won't fit
112 */
113
114int sasl_encode64(const char *_in, unsigned inlen,
115		  char *_out, unsigned outmax, unsigned *outlen)
116{
117    const unsigned char *in = (const unsigned char *)_in;
118    unsigned char *out = (unsigned char *)_out;
119    unsigned char oval;
120    char *blah;
121    unsigned olen;
122
123    /* check params */
124    if ((inlen >0) && (in == NULL)) return SASL_BADPARAM;
125
126    /* Will it fit? */
127    olen = (inlen + 2) / 3 * 4;
128    if (outlen) {
129      *outlen = olen;
130    }
131    if (outmax <= olen) {
132      return SASL_BUFOVER;
133    }
134
135    /* Do the work... */
136    blah=(char *) out;
137    while (inlen >= 3) {
138      /* user provided max buffer size; make sure we don't go over it */
139        *out++ = basis_64[in[0] >> 2];
140        *out++ = basis_64[((in[0] << 4) & 0x30) | (in[1] >> 4)];
141        *out++ = basis_64[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
142        *out++ = basis_64[in[2] & 0x3f];
143        in += 3;
144        inlen -= 3;
145    }
146    if (inlen > 0) {
147      /* user provided max buffer size; make sure we don't go over it */
148        *out++ = basis_64[in[0] >> 2];
149        oval = (in[0] << 4) & 0x30;
150        if (inlen > 1) oval |= in[1] >> 4;
151        *out++ = basis_64[oval];
152        *out++ = (inlen < 2) ? '=' : basis_64[(in[1] << 2) & 0x3c];
153        *out++ = '=';
154    }
155
156    *out = '\0';
157
158    return SASL_OK;
159}
160
161/* base64 decode
162 *  in     -- input data
163 *  inlen  -- length of input data
164 *  out    -- output data (may be same as in, must have enough space)
165 *  outmax  -- max size of output buffer
166 * result:
167 *  outlen -- actual output length
168 *
169 * returns:
170 * SASL_BADPROT on bad base64,
171 * SASL_BUFOVER if result won't fit,
172 * SASL_OK on success
173 */
174
175int sasl_decode64(const char *in, unsigned inlen,
176		  char *out, unsigned outmax, unsigned *outlen)
177{
178    unsigned len = 0,lup;
179    int c1, c2, c3, c4;
180
181    /* check parameters */
182    if (out==NULL) return SASL_FAIL;
183
184    /* xxx these necessary? */
185    if (in[0] == '+' && in[1] == ' ') in += 2;
186    if (*in == '\r') return SASL_FAIL;
187
188    for (lup=0;lup<inlen/4;lup++)
189    {
190        c1 = in[0];
191        if (CHAR64(c1) == -1) return SASL_BADPROT;
192        c2 = in[1];
193        if (CHAR64(c2) == -1) return SASL_BADPROT;
194        c3 = in[2];
195        if (c3 != '=' && CHAR64(c3) == -1) return SASL_BADPROT;
196        c4 = in[3];
197        if (c4 != '=' && CHAR64(c4) == -1) return SASL_BADPROT;
198        in += 4;
199        *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
200        if(++len >= outmax) return SASL_BUFOVER;
201        if (c3 != '=') {
202            *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
203            if(++len >= outmax) return SASL_BUFOVER;
204            if (c4 != '=') {
205                *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
206                if(++len >= outmax) return SASL_BUFOVER;
207            }
208        }
209    }
210
211    *out=0; /* terminate string */
212
213    if(outlen) *outlen=len;
214
215    return SASL_OK;
216}
217
218/* make a challenge string (NUL terminated)
219 *  buf      -- buffer for result
220 *  maxlen   -- max length of result
221 *  hostflag -- 0 = don't include hostname, 1 = include hostname
222 * returns final length or 0 if not enough space
223 */
224
225int sasl_mkchal(sasl_conn_t *conn,
226		char *buf,
227		unsigned maxlen,
228		unsigned hostflag)
229{
230  sasl_rand_t *pool = NULL;
231  unsigned long randnum;
232  int ret;
233  time_t now;
234  unsigned len;
235
236  len = 4			/* <.>\0 */
237    + (2 * 20);			/* 2 numbers, 20 => max size of 64bit
238				 * ulong in base 10 */
239  if (hostflag && conn->serverFQDN)
240    len += (unsigned) strlen(conn->serverFQDN) + 1 /* for the @ */;
241
242  if (maxlen < len)
243    return 0;
244
245  ret = sasl_randcreate(&pool);
246  if(ret != SASL_OK) return 0; /* xxx sasl return code? */
247
248  sasl_rand(pool, (char *)&randnum, sizeof(randnum));
249  sasl_randfree(&pool);
250
251  time(&now);
252
253  if (hostflag && conn->serverFQDN)
254    snprintf(buf,maxlen, "<%lu.%lu@%s>", randnum, now, conn->serverFQDN);
255  else
256    snprintf(buf,maxlen, "<%lu.%lu>", randnum, now);
257
258  return (int) strlen(buf);
259}
260
261  /* borrowed from larry. probably works :)
262   * probably is also in acap server somewhere
263   */
264int sasl_utf8verify(const char *str, unsigned len)
265{
266  unsigned i;
267  for (i = 0; i < len; i++) {
268    /* how many octets? */
269    int seqlen = 0;
270    while (str[i] & (0x80 >> seqlen)) ++seqlen;
271    if (seqlen == 0) continue; /* this is a valid US-ASCII char */
272    if (seqlen == 1) return SASL_BADPROT; /* this shouldn't happen here */
273    if (seqlen > 6) return SASL_BADPROT; /* illegal */
274    while (--seqlen)
275      if ((str[++i] & 0xC0) != 0xF0) return SASL_BADPROT; /* needed a 10 octet */
276  }
277  return SASL_OK;
278}
279
280/*
281 * To see why this is really bad see RFC 1750
282 *
283 * unfortunatly there currently is no way to make
284 * cryptographically secure pseudo random numbers
285 * without specialized hardware etc...
286 * thus, this is for nonce use only
287 */
288void getranddata(unsigned short ret[RPOOL_SIZE])
289{
290    long curtime;
291
292    memset(ret, 0, RPOOL_SIZE*sizeof(unsigned short));
293
294#ifdef DEV_RANDOM
295    {
296	int fd;
297
298	fd = open(DEV_RANDOM, O_RDONLY);
299	if(fd != -1) {
300	    unsigned char *buf = (unsigned char *)ret;
301	    ssize_t bytesread = 0;
302	    size_t bytesleft = RPOOL_SIZE*sizeof(unsigned short);
303
304	    do {
305		bytesread = read(fd, buf, bytesleft);
306		if(bytesread == -1 && errno == EINTR) continue;
307		else if(bytesread <= 0) break;
308		bytesleft -= bytesread;
309		buf += bytesread;
310	    } while(bytesleft != 0);
311
312	    close(fd);
313	}
314    }
315#endif
316
317#ifdef HAVE_GETPID
318    ret[0] ^= (unsigned short) getpid();
319#endif
320
321#ifdef HAVE_GETTIMEOFDAY
322    {
323	struct timeval tv;
324
325	/* xxx autoconf macro */
326#ifdef _SVID_GETTOD
327	if (!gettimeofday(&tv))
328#else
329	if (!gettimeofday(&tv, NULL))
330#endif
331	{
332	    /* longs are guaranteed to be at least 32 bits; we need
333	       16 bits in each short */
334	    ret[0] ^= (unsigned short) (tv.tv_sec & 0xFFFF);
335	    ret[1] ^= (unsigned short) (clock() & 0xFFFF);
336	    ret[1] ^= (unsigned short) (tv.tv_usec >> 16);
337	    ret[2] ^= (unsigned short) (tv.tv_usec & 0xFFFF);
338	    return;
339	}
340    }
341#endif /* HAVE_GETTIMEOFDAY */
342
343    /* if all else fails just use time() */
344    curtime = (long) time(NULL); /* better be at least 32 bits */
345
346    ret[0] ^= (unsigned short) (curtime >> 16);
347    ret[1] ^= (unsigned short) (curtime & 0xFFFF);
348    ret[2] ^= (unsigned short) (clock() & 0xFFFF);
349
350    return;
351}
352
353int sasl_randcreate(sasl_rand_t **rpool)
354{
355  (*rpool)=sasl_ALLOC(sizeof(sasl_rand_t));
356  if ((*rpool) == NULL) return SASL_NOMEM;
357
358  /* init is lazy */
359  (*rpool)->initialized = 0;
360
361  return SASL_OK;
362}
363
364void sasl_randfree(sasl_rand_t **rpool)
365{
366    sasl_FREE(*rpool);
367}
368
369void sasl_randseed (sasl_rand_t *rpool, const char *seed, unsigned len)
370{
371    /* is it acceptable to just use the 1st 3 char's given??? */
372    unsigned int lup;
373
374    /* check params */
375    if (seed == NULL) return;
376    if (rpool == NULL) return;
377
378    rpool->initialized = 1;
379
380    if (len > sizeof(unsigned short)*RPOOL_SIZE)
381      len = sizeof(unsigned short)*RPOOL_SIZE;
382
383    for (lup = 0; lup < len; lup += 2)
384	rpool->pool[lup/2] = (seed[lup] << 8) + seed[lup + 1];
385}
386
387static void randinit(sasl_rand_t *rpool)
388{
389    if (!rpool) return;
390
391    if (!rpool->initialized) {
392	getranddata(rpool->pool);
393	rpool->initialized = 1;
394#if !(defined(WIN32)||defined(macintosh))
395#ifndef HAVE_JRAND48
396    {
397      /* xxx varies by platform */
398	unsigned int *foo = (unsigned int *)rpool->pool;
399	srandom(*foo);
400    }
401#endif /* HAVE_JRAND48 */
402#endif /* WIN32 */
403    }
404
405}
406
407void sasl_rand (sasl_rand_t *rpool, char *buf, unsigned len)
408{
409    unsigned int lup;
410    /* check params */
411    if (!rpool || !buf) return;
412
413    /* init if necessary */
414    randinit(rpool);
415
416#if (defined(WIN32)||defined(macintosh))
417    for (lup=0;lup<len;lup++)
418	buf[lup] = (char) (rand() >> 8);
419#else /* WIN32 */
420#ifdef HAVE_JRAND48
421    for (lup=0; lup<len; lup++)
422	buf[lup] = (char) (jrand48(rpool->pool) >> 8);
423#else
424    for (lup=0;lup<len;lup++)
425	buf[lup] = (char) (random() >> 8);
426#endif /* HAVE_JRAND48 */
427#endif /* WIN32 */
428}
429
430/* this function is just a bad idea all around, since we're not trying to
431   implement a true random number generator */
432void sasl_churn (sasl_rand_t *rpool, const char *data, unsigned len)
433{
434    unsigned int lup;
435
436    /* check params */
437    if (!rpool || !data) return;
438
439    /* init if necessary */
440    randinit(rpool);
441
442    for (lup=0; lup<len; lup++)
443	rpool->pool[lup % RPOOL_SIZE] ^= data[lup];
444}
445
446void sasl_erasebuffer(char *buf, unsigned len) {
447    memset(buf, 0, len);
448}
449
450#ifdef WIN32
451/*****************************************************************************
452 *
453 *  MODULE NAME : GETOPT.C
454 *
455 *  COPYRIGHTS:
456 *             This module contains code made available by IBM
457 *             Corporation on an AS IS basis.  Any one receiving the
458 *             module is considered to be licensed under IBM copyrights
459 *             to use the IBM-provided source code in any way he or she
460 *             deems fit, including copying it, compiling it, modifying
461 *             it, and redistributing it, with or without
462 *             modifications.  No license under any IBM patents or
463 *             patent applications is to be implied from this copyright
464 *             license.
465 *
466 *             A user of the module should understand that IBM cannot
467 *             provide technical support for the module and will not be
468 *             responsible for any consequences of use of the program.
469 *
470 *             Any notices, including this one, are not to be removed
471 *             from the module without the prior written consent of
472 *             IBM.
473 *
474 *  AUTHOR:   Original author:
475 *                 G. R. Blair (BOBBLAIR at AUSVM1)
476 *                 Internet: bobblair@bobblair.austin.ibm.com
477 *
478 *            Extensively revised by:
479 *                 John Q. Walker II, Ph.D. (JOHHQ at RALVM6)
480 *                 Internet: johnq@ralvm6.vnet.ibm.com
481 *
482 *****************************************************************************/
483
484/******************************************************************************
485 * getopt()
486 *
487 * The getopt() function is a command line parser.  It returns the next
488 * option character in argv that matches an option character in opstring.
489 *
490 * The argv argument points to an array of argc+1 elements containing argc
491 * pointers to character strings followed by a null pointer.
492 *
493 * The opstring argument points to a string of option characters; if an
494 * option character is followed by a colon, the option is expected to have
495 * an argument that may or may not be separated from it by white space.
496 * The external variable optarg is set to point to the start of the option
497 * argument on return from getopt().
498 *
499 * The getopt() function places in optind the argv index of the next argument
500 * to be processed.  The system initializes the external variable optind to
501 * 1 before the first call to getopt().
502 *
503 * When all options have been processed (that is, up to the first nonoption
504 * argument), getopt() returns EOF.  The special option "--" may be used to
505 * delimit the end of the options; EOF will be returned, and "--" will be
506 * skipped.
507 *
508 * The getopt() function returns a question mark (?) when it encounters an
509 * option character not included in opstring.  This error message can be
510 * disabled by setting opterr to zero.  Otherwise, it returns the option
511 * character that was detected.
512 *
513 * If the special option "--" is detected, or all options have been
514 * processed, EOF is returned.
515 *
516 * Options are marked by either a minus sign (-) or a slash (/).
517 *
518 * No errors are defined.
519 *****************************************************************************/
520
521#include <string.h>                 /* for strchr() */
522
523/* static (global) variables that are specified as exported by getopt() */
524__declspec(dllexport) char *optarg = NULL;    /* pointer to the start of the option argument  */
525__declspec(dllexport) int   optind = 1;       /* number of the next argv[] to be evaluated    */
526__declspec(dllexport) int   opterr = 1;       /* non-zero if a question mark should be returned */
527
528
529/* handle possible future character set concerns by putting this in a macro */
530#define _next_char(string)  (char)(*(string+1))
531
532int getopt(int argc, char *argv[], char *opstring)
533{
534    static char *pIndexPosition = NULL; /* place inside current argv string */
535    char *pArgString = NULL;        /* where to start from next */
536    char *pOptString;               /* the string in our program */
537
538
539    if (pIndexPosition != NULL) {
540        /* we last left off inside an argv string */
541        if (*(++pIndexPosition)) {
542            /* there is more to come in the most recent argv */
543            pArgString = pIndexPosition;
544        }
545    }
546
547    if (pArgString == NULL) {
548        /* we didn't leave off in the middle of an argv string */
549        if (optind >= argc) {
550            /* more command-line arguments than the argument count */
551            pIndexPosition = NULL;  /* not in the middle of anything */
552            return EOF;             /* used up all command-line arguments */
553        }
554
555        /*---------------------------------------------------------------------
556         * If the next argv[] is not an option, there can be no more options.
557         *-------------------------------------------------------------------*/
558        pArgString = argv[optind++]; /* set this to the next argument ptr */
559
560        if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */
561            ('-' != *pArgString)) {
562            --optind;               /* point to current arg once we're done */
563            optarg = NULL;          /* no argument follows the option */
564            pIndexPosition = NULL;  /* not in the middle of anything */
565            return EOF;             /* used up all the command-line flags */
566        }
567
568        /* check for special end-of-flags markers */
569        if ((strcmp(pArgString, "-") == 0) ||
570            (strcmp(pArgString, "--") == 0)) {
571            optarg = NULL;          /* no argument follows the option */
572            pIndexPosition = NULL;  /* not in the middle of anything */
573            return EOF;             /* encountered the special flag */
574        }
575
576        pArgString++;               /* look past the / or - */
577    }
578
579    if (':' == *pArgString) {       /* is it a colon? */
580        /*---------------------------------------------------------------------
581         * Rare case: if opterr is non-zero, return a question mark;
582         * otherwise, just return the colon we're on.
583         *-------------------------------------------------------------------*/
584        return (opterr ? (int)'?' : (int)':');
585    }
586    else if ((pOptString = strchr(opstring, *pArgString)) == 0) {
587        /*---------------------------------------------------------------------
588         * The letter on the command-line wasn't any good.
589         *-------------------------------------------------------------------*/
590        optarg = NULL;              /* no argument follows the option */
591        pIndexPosition = NULL;      /* not in the middle of anything */
592        return (opterr ? (int)'?' : (int)*pArgString);
593    }
594    else {
595        /*---------------------------------------------------------------------
596         * The letter on the command-line matches one we expect to see
597         *-------------------------------------------------------------------*/
598        if (':' == _next_char(pOptString)) { /* is the next letter a colon? */
599            /* It is a colon.  Look for an argument string. */
600            if ('\0' != _next_char(pArgString)) {  /* argument in this argv? */
601                optarg = &pArgString[1];   /* Yes, it is */
602            }
603            else {
604                /*-------------------------------------------------------------
605                 * The argument string must be in the next argv.
606                 * But, what if there is none (bad input from the user)?
607                 * In that case, return the letter, and optarg as NULL.
608                 *-----------------------------------------------------------*/
609                if (optind < argc)
610                    optarg = argv[optind++];
611                else {
612                    optarg = NULL;
613                    return (opterr ? (int)'?' : (int)*pArgString);
614                }
615            }
616            pIndexPosition = NULL;  /* not in the middle of anything */
617        }
618        else {
619            /* it's not a colon, so just return the letter */
620            optarg = NULL;          /* no argument follows the option */
621            pIndexPosition = pArgString;    /* point to the letter we're on */
622        }
623        return (int)*pArgString;    /* return the letter that matched */
624    }
625}
626
627#ifndef PASSWORD_MAX
628#  define PASSWORD_MAX 255
629#endif
630
631#include <conio.h>
632char *
633getpass(prompt)
634const char *prompt;
635{
636	register char *p;
637	register c;
638	static char pbuf[PASSWORD_MAX];
639
640	fprintf(stderr, "%s", prompt); (void) fflush(stderr);
641	for (p=pbuf; (c = _getch())!=13 && c!=EOF;) {
642		if (p < &pbuf[sizeof(pbuf)-1])
643			*p++ = c;
644	}
645	*p = '\0';
646	fprintf(stderr, "\n"); (void) fflush(stderr);
647	return(pbuf);
648}
649
650
651
652#endif /* WIN32 */
653