source: trunk/third/cyrus-sasl/lib/saslutil.c @ 18842

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