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
49#if defined(WIN32)
50#define _CRT_RAND_S
51#endif
52
53#include <stdlib.h>
54#include <string.h>
55#include <ctype.h>
56#include <sys/types.h>
57#include <sys/stat.h>
58#include <fcntl.h>
59#include <errno.h>
60#ifdef HAVE_UNISTD_H
61#include <unistd.h>
62#endif
63#ifdef HAVE_TIME_H
64#include <time.h>
65#endif
66#include "saslint.h"
67#include <saslutil.h>
68
69/*  Contains:
70 *
71 * sasl_decode64
72 * sasl_encode64
73 * sasl_mkchal
74 * sasl_utf8verify
75 * sasl_randcreate
76 * sasl_randfree
77 * sasl_randseed
78 * sasl_rand
79 * sasl_churn
80 * sasl_erasebuffer
81 */
82
83#ifdef sun
84/* gotta define gethostname ourselves on suns */
85extern int gethostname(char *, int);
86#endif
87
88char *encode_table;
89char *decode_table;
90
91#define RPOOL_SIZE 3
92struct sasl_rand_s {
93    unsigned short pool[RPOOL_SIZE];
94    /* since the init time might be really bad let's make this lazy */
95    int initialized;
96};
97
98#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
99
100static char basis_64[] =
101   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";
102
103static char index_64[128] = {
104    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
105    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
106    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
107    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
108    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
109    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
110    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
111    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
112};
113
114/* base64 encode
115 *  in      -- input data
116 *  inlen   -- input data length
117 *  out     -- output buffer (will be NUL terminated)
118 *  outmax  -- max size of output buffer
119 * result:
120 *  outlen  -- gets actual length of output buffer (optional)
121 *
122 * Returns SASL_OK on success, SASL_BUFOVER if result won't fit
123 */
124
125int sasl_encode64(const char *_in,
126		  unsigned inlen,
127		  char *_out,
128		  unsigned outmax,
129		  unsigned *outlen)
130{
131    const unsigned char *in = (const unsigned char *)_in;
132    unsigned char *out = (unsigned char *)_out;
133    unsigned char oval;
134    char *blah;
135    unsigned olen;
136
137    /* check params */
138    if ((inlen > 0) && (in == NULL)) return SASL_BADPARAM;
139
140    /* Will it fit? */
141    olen = (inlen + 2) / 3 * 4;
142    if (outlen) {
143	*outlen = olen;
144    }
145    if (outmax <= olen) {
146	return SASL_BUFOVER;
147    }
148
149    /* Do the work... */
150    blah = (char *) out;
151    while (inlen >= 3) {
152      /* user provided max buffer size; make sure we don't go over it */
153        *out++ = basis_64[in[0] >> 2];
154        *out++ = basis_64[((in[0] << 4) & 0x30) | (in[1] >> 4)];
155        *out++ = basis_64[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
156        *out++ = basis_64[in[2] & 0x3f];
157        in += 3;
158        inlen -= 3;
159    }
160    if (inlen > 0) {
161      /* user provided max buffer size; make sure we don't go over it */
162        *out++ = basis_64[in[0] >> 2];
163        oval = (in[0] << 4) & 0x30;
164        if (inlen > 1) oval |= in[1] >> 4;
165        *out++ = basis_64[oval];
166        *out++ = (inlen < 2) ? '=' : basis_64[(in[1] << 2) & 0x3c];
167        *out++ = '=';
168    }
169
170    *out = '\0';
171
172    return SASL_OK;
173}
174
175/* base64 decode
176 *  in     -- input data
177 *  inlen  -- length of input data
178 *  out    -- output data (may be same as in, must have enough space)
179 *  outmax  -- max size of output buffer
180 * result:
181 *  outlen -- actual output length
182 *
183 * returns:
184 * SASL_BADPROT on bad base64,
185 * SASL_BUFOVER if result won't fit,
186 * SASL_CONTINUE on a partial block,
187 * SASL_OK on success
188 */
189
190int sasl_decode64(const char *in,
191                  unsigned inlen,
192                  char *out,
193                  unsigned outmax,  /* size of the buffer, not counting the NUL */
194                  unsigned *outlen)
195{
196    unsigned len = 0;
197    unsigned j;
198    int c[4];
199    int saw_equal = 0;
200
201    /* check parameters */
202    if (out == NULL) return SASL_FAIL;
203
204    if (inlen > 0 && *in == '\r') return SASL_FAIL;
205
206    while (inlen > 3) {
207        /* No data is valid after an '=' character */
208        if (saw_equal) {
209            return SASL_BADPROT;
210        }
211
212	for (j = 0; j < 4; j++) {
213	    c[j] = in[0];
214	    in++;
215	    inlen--;
216	}
217
218        if (CHAR64(c[0]) == -1 || CHAR64(c[1]) == -1) return SASL_BADPROT;
219        if (c[2] != '=' && CHAR64(c[2]) == -1) return SASL_BADPROT;
220        if (c[3] != '=' && CHAR64(c[3]) == -1) return SASL_BADPROT;
221        /* No data is valid after a '=' character, unless it is another '=' */
222        if (c[2] == '=' && c[3] != '=') return SASL_BADPROT;
223        if (c[2] == '=' || c[3] == '=') {
224            saw_equal = 1;
225        }
226
227        *out++ = (CHAR64(c[0]) << 2) | (CHAR64(c[1]) >> 4);
228        if (++len >= outmax) return SASL_BUFOVER;
229        if (c[2] != '=') {
230            *out++ = ((CHAR64(c[1]) << 4) & 0xf0) | (CHAR64(c[2]) >> 2);
231            if (++len >= outmax) return SASL_BUFOVER;
232            if (c[3] != '=') {
233                *out++ = ((CHAR64(c[2]) << 6) & 0xc0) | CHAR64(c[3]);
234                if (++len >= outmax) return SASL_BUFOVER;
235            }
236        }
237    }
238
239    *out = '\0'; /* NUL terminate the output string */
240
241    if (outlen) *outlen = len;
242
243    if (inlen != 0) {
244        if (saw_equal) {
245            /* Unless there is CRLF at the end? */
246            return SASL_BADPROT;
247        } else {
248	    return (SASL_CONTINUE);
249        }
250    }
251
252    return SASL_OK;
253}
254
255/* make a challenge string (NUL terminated)
256 *  buf      -- buffer for result
257 *  maxlen   -- max length of result
258 *  hostflag -- 0 = don't include hostname, 1 = include hostname
259 * returns final length or 0 if not enough space
260 */
261
262int sasl_mkchal(sasl_conn_t *conn,
263		char *buf,
264		unsigned maxlen,
265		unsigned hostflag)
266{
267  sasl_rand_t *pool = NULL;
268  unsigned long randnum;
269  int ret;
270  time_t now;
271  unsigned len;
272
273  len = 4			/* <.>\0 */
274    + (2 * 20);			/* 2 numbers, 20 => max size of 64bit
275				 * ulong in base 10 */
276  if (hostflag && conn->serverFQDN)
277    len += (unsigned) strlen(conn->serverFQDN) + 1 /* for the @ */;
278
279  if (maxlen < len)
280    return 0;
281
282  ret = sasl_randcreate(&pool);
283  if(ret != SASL_OK) return 0; /* xxx sasl return code? */
284
285  sasl_rand(pool, (char *)&randnum, sizeof(randnum));
286  sasl_randfree(&pool);
287
288  time(&now);
289
290  if (hostflag && conn->serverFQDN)
291    snprintf(buf,maxlen, "<%lu.%lu@%s>", randnum, now, conn->serverFQDN);
292  else
293    snprintf(buf,maxlen, "<%lu.%lu>", randnum, now);
294
295  return (int) strlen(buf);
296}
297
298  /* borrowed from larry. probably works :)
299   * probably is also in acap server somewhere
300   */
301int sasl_utf8verify(const char *str, unsigned len)
302{
303  unsigned i;
304  for (i = 0; i < len; i++) {
305    /* how many octets? */
306    int seqlen = 0;
307    while (str[i] & (0x80 >> seqlen)) ++seqlen;
308    if (seqlen == 0) continue; /* this is a valid US-ASCII char */
309    if (seqlen == 1) return SASL_BADPROT; /* this shouldn't happen here */
310    if (seqlen > 6) return SASL_BADPROT; /* illegal */
311    while (--seqlen)
312      if ((str[++i] & 0xC0) != 0xF0) return SASL_BADPROT; /* needed a 10 octet */
313  }
314  return SASL_OK;
315}
316
317/*
318 * To see why this is really bad see RFC 1750
319 *
320 * unfortunatly there currently is no way to make
321 * cryptographically secure pseudo random numbers
322 * without specialized hardware etc...
323 * thus, this is for nonce use only
324 */
325void getranddata(unsigned short ret[RPOOL_SIZE])
326{
327    long curtime;
328
329    memset(ret, 0, RPOOL_SIZE*sizeof(unsigned short));
330
331#ifdef DEV_RANDOM
332    {
333	int fd;
334
335	fd = open(DEV_RANDOM, O_RDONLY);
336	if(fd != -1) {
337	    unsigned char *buf = (unsigned char *)ret;
338	    ssize_t bytesread = 0;
339	    size_t bytesleft = RPOOL_SIZE*sizeof(unsigned short);
340
341	    do {
342		bytesread = read(fd, buf, bytesleft);
343		if(bytesread == -1 && errno == EINTR) continue;
344		else if(bytesread <= 0) break;
345		bytesleft -= bytesread;
346		buf += bytesread;
347	    } while(bytesleft != 0);
348
349	    close(fd);
350	}
351    }
352#endif
353
354#ifdef HAVE_GETPID
355    ret[0] ^= (unsigned short) getpid();
356#endif
357
358#ifdef HAVE_GETTIMEOFDAY
359    {
360	struct timeval tv;
361
362	/* xxx autoconf macro */
363#ifdef _SVID_GETTOD
364	if (!gettimeofday(&tv))
365#else
366	if (!gettimeofday(&tv, NULL))
367#endif
368	{
369	    /* longs are guaranteed to be at least 32 bits; we need
370	       16 bits in each short */
371	    ret[0] ^= (unsigned short) (tv.tv_sec & 0xFFFF);
372	    ret[1] ^= (unsigned short) (clock() & 0xFFFF);
373	    ret[1] ^= (unsigned short) (tv.tv_usec >> 16);
374	    ret[2] ^= (unsigned short) (tv.tv_usec & 0xFFFF);
375	    return;
376	}
377    }
378#endif /* HAVE_GETTIMEOFDAY */
379
380    /* if all else fails just use time() */
381    curtime = (long) time(NULL); /* better be at least 32 bits */
382
383    ret[0] ^= (unsigned short) (curtime >> 16);
384    ret[1] ^= (unsigned short) (curtime & 0xFFFF);
385    ret[2] ^= (unsigned short) (clock() & 0xFFFF);
386
387    return;
388}
389
390int sasl_randcreate(sasl_rand_t **rpool)
391{
392  (*rpool)=sasl_ALLOC(sizeof(sasl_rand_t));
393  if ((*rpool) == NULL) return SASL_NOMEM;
394
395  /* init is lazy */
396  (*rpool)->initialized = 0;
397
398  return SASL_OK;
399}
400
401void sasl_randfree(sasl_rand_t **rpool)
402{
403    sasl_FREE(*rpool);
404}
405
406void sasl_randseed (sasl_rand_t *rpool, const char *seed, unsigned len)
407{
408    /* is it acceptable to just use the 1st 3 char's given??? */
409    unsigned int lup;
410
411    /* check params */
412    if (seed == NULL) return;
413    if (rpool == NULL) return;
414
415    rpool->initialized = 1;
416
417    if (len > sizeof(unsigned short)*RPOOL_SIZE)
418      len = sizeof(unsigned short)*RPOOL_SIZE;
419
420    for (lup = 0; lup < len; lup += 2)
421	rpool->pool[lup/2] = (seed[lup] << 8) + seed[lup + 1];
422}
423
424static void randinit(sasl_rand_t *rpool)
425{
426    if (!rpool) return;
427
428    if (!rpool->initialized) {
429	getranddata(rpool->pool);
430	rpool->initialized = 1;
431#if !(defined(WIN32)||defined(macintosh))
432#ifndef HAVE_JRAND48
433    {
434      /* xxx varies by platform */
435	unsigned int *foo = (unsigned int *)rpool->pool;
436	srandom(*foo);
437    }
438#endif /* HAVE_JRAND48 */
439#elif defined(WIN32)
440    {
441	unsigned int *foo = (unsigned int *)rpool->pool;
442	srand(*foo);
443    }
444#endif /* WIN32 */
445    }
446
447}
448
449void sasl_rand (sasl_rand_t *rpool, char *buf, unsigned len)
450{
451    unsigned int lup;
452#if defined(WIN32) && !defined(__MINGW32__)
453    unsigned int randomValue;
454#endif
455
456    /* check params */
457    if (!rpool || !buf) return;
458
459    /* init if necessary */
460    randinit(rpool);
461
462    for (lup = 0; lup < len; lup++) {
463#if defined(__MINGW32__)
464	buf[lup] = (char) (rand() >> 8);
465#elif defined(WIN32)
466	if (rand_s(&randomValue) != 0) {
467	    randomValue = rand();
468	}
469
470	buf[lup] = (char) (randomValue >> 8);
471#elif defined(macintosh)
472	buf[lup] = (char) (rand() >> 8);
473#else /* !WIN32 && !macintosh */
474#ifdef HAVE_JRAND48
475	buf[lup] = (char) (jrand48(rpool->pool) >> 8);
476#else
477	buf[lup] = (char) (random() >> 8);
478#endif /* HAVE_JRAND48 */
479#endif /* WIN32 */
480    }
481}
482
483/* this function is just a bad idea all around, since we're not trying to
484   implement a true random number generator */
485void sasl_churn (sasl_rand_t *rpool, const char *data, unsigned len)
486{
487    unsigned int lup;
488
489    /* check params */
490    if (!rpool || !data) return;
491
492    /* init if necessary */
493    randinit(rpool);
494
495    for (lup=0; lup<len; lup++)
496	rpool->pool[lup % RPOOL_SIZE] ^= data[lup];
497}
498
499void sasl_erasebuffer(char *buf, unsigned len) {
500    memset(buf, 0, len);
501}
502
503/* Lowercase string in place */
504char *sasl_strlower (
505  char *val
506)
507{
508    int i;
509
510    if (val == NULL) {
511	return (NULL);
512    }
513
514/* don't use tolower(), as it is locale dependent */
515
516    for (i = 0; val[i] != '\0'; i++) {
517	if (val[i] >= 'A' && val[i] <= 'Z') {
518	    val[i] = val[i] - 'A' + 'a';
519	}
520    }
521
522    return (val);
523}
524
525/* A version of gethostname that tries hard to return a FQDN */
526int get_fqhostname(
527  char *name,
528  int namelen,
529  int abort_if_no_fqdn
530)
531{
532    int return_value;
533    struct addrinfo hints;
534    struct addrinfo *result;
535
536    return_value = gethostname (name, namelen);
537    if (return_value != 0) {
538	return (return_value);
539    }
540
541    if (strchr (name, '.') != NULL) {
542	goto LOWERCASE;
543    }
544
545/* gethostname hasn't returned a FQDN, we have to canonify it ourselves */
546    hints.ai_family = PF_UNSPEC;
547    hints.ai_flags = AI_CANONNAME;
548    hints.ai_socktype = SOCK_STREAM;	/* TCP only */
549/* A value of zero for ai_protocol indicates the caller will accept any protocol. or IPPROTO_TCP? */
550    hints.ai_protocol = 0;  /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
551    hints.ai_addrlen = 0;
552    hints.ai_canonname = NULL;
553    hints.ai_addr = NULL;
554    hints.ai_next = NULL;
555
556    if (getaddrinfo(name,
557		  NULL,		/* don't care abour service/port */
558		  &hints,
559		  &result) != 0) {
560        if (abort_if_no_fqdn) {
561	    /* errno on Unix, WSASetLastError on Windows are already done by the function */
562	    return (-1);
563	} else {
564	    goto LOWERCASE;
565	}
566    }
567
568    if (result == NULL || result->ai_canonname == NULL) {
569	freeaddrinfo (result);
570        if (abort_if_no_fqdn) {
571#ifdef WIN32
572	    WSASetLastError (WSANO_DATA);
573#elif defined(ENODATA)
574	    errno = ENODATA;
575#elif defined(EADDRNOTAVAIL)
576	    errno = EADDRNOTAVAIL;
577#endif
578	    return (-1);
579	} else {
580	    goto LOWERCASE;
581	}
582    }
583
584    if (strchr (result->ai_canonname, '.') == NULL) {
585	freeaddrinfo (result);
586        if (abort_if_no_fqdn) {
587#ifdef WIN32
588	    WSASetLastError (WSANO_DATA);
589#elif defined(ENODATA)
590	    errno = ENODATA;
591#elif defined(EADDRNOTAVAIL)
592	    errno = EADDRNOTAVAIL;
593#endif
594	    return (-1);
595	} else {
596	    goto LOWERCASE;
597	}
598    }
599
600
601/* Do we need to check for buffer overflow and set errno? */
602    strncpy (name, result->ai_canonname, namelen);
603    freeaddrinfo (result);
604
605LOWERCASE:
606    //sasl_strlower (name); // Also see _sasl_conn_init(), disabled for <rdar://problem/16871259>
607    return (0);
608}
609
610#ifdef WIN32
611/*****************************************************************************
612 *
613 *  MODULE NAME : GETOPT.C
614 *
615 *  COPYRIGHTS:
616 *             This module contains code made available by IBM
617 *             Corporation on an AS IS basis.  Any one receiving the
618 *             module is considered to be licensed under IBM copyrights
619 *             to use the IBM-provided source code in any way he or she
620 *             deems fit, including copying it, compiling it, modifying
621 *             it, and redistributing it, with or without
622 *             modifications.  No license under any IBM patents or
623 *             patent applications is to be implied from this copyright
624 *             license.
625 *
626 *             A user of the module should understand that IBM cannot
627 *             provide technical support for the module and will not be
628 *             responsible for any consequences of use of the program.
629 *
630 *             Any notices, including this one, are not to be removed
631 *             from the module without the prior written consent of
632 *             IBM.
633 *
634 *  AUTHOR:   Original author:
635 *                 G. R. Blair (BOBBLAIR at AUSVM1)
636 *                 Internet: bobblair@bobblair.austin.ibm.com
637 *
638 *            Extensively revised by:
639 *                 John Q. Walker II, Ph.D. (JOHHQ at RALVM6)
640 *                 Internet: johnq@ralvm6.vnet.ibm.com
641 *
642 *****************************************************************************/
643
644/******************************************************************************
645 * getopt()
646 *
647 * The getopt() function is a command line parser.  It returns the next
648 * option character in argv that matches an option character in opstring.
649 *
650 * The argv argument points to an array of argc+1 elements containing argc
651 * pointers to character strings followed by a null pointer.
652 *
653 * The opstring argument points to a string of option characters; if an
654 * option character is followed by a colon, the option is expected to have
655 * an argument that may or may not be separated from it by white space.
656 * The external variable optarg is set to point to the start of the option
657 * argument on return from getopt().
658 *
659 * The getopt() function places in optind the argv index of the next argument
660 * to be processed.  The system initializes the external variable optind to
661 * 1 before the first call to getopt().
662 *
663 * When all options have been processed (that is, up to the first nonoption
664 * argument), getopt() returns EOF.  The special option "--" may be used to
665 * delimit the end of the options; EOF will be returned, and "--" will be
666 * skipped.
667 *
668 * The getopt() function returns a question mark (?) when it encounters an
669 * option character not included in opstring.  This error message can be
670 * disabled by setting opterr to zero.  Otherwise, it returns the option
671 * character that was detected.
672 *
673 * If the special option "--" is detected, or all options have been
674 * processed, EOF is returned.
675 *
676 * Options are marked by either a minus sign (-) or a slash (/).
677 *
678 * No errors are defined.
679 *****************************************************************************/
680
681#include <string.h>                 /* for strchr() */
682
683/* static (global) variables that are specified as exported by getopt() */
684__declspec(dllexport) char *optarg = NULL;    /* pointer to the start of the option argument  */
685__declspec(dllexport) int   optind = 1;       /* number of the next argv[] to be evaluated    */
686__declspec(dllexport) int   opterr = 1;       /* non-zero if a question mark should be returned */
687
688
689/* handle possible future character set concerns by putting this in a macro */
690#define _next_char(string)  (char)(*(string+1))
691
692int getopt(int argc, char *argv[], char *opstring)
693{
694    static char *pIndexPosition = NULL; /* place inside current argv string */
695    char *pArgString = NULL;        /* where to start from next */
696    char *pOptString;               /* the string in our program */
697
698
699    if (pIndexPosition != NULL) {
700        /* we last left off inside an argv string */
701        if (*(++pIndexPosition)) {
702            /* there is more to come in the most recent argv */
703            pArgString = pIndexPosition;
704        }
705    }
706
707    if (pArgString == NULL) {
708        /* we didn't leave off in the middle of an argv string */
709        if (optind >= argc) {
710            /* more command-line arguments than the argument count */
711            pIndexPosition = NULL;  /* not in the middle of anything */
712            return EOF;             /* used up all command-line arguments */
713        }
714
715        /*---------------------------------------------------------------------
716         * If the next argv[] is not an option, there can be no more options.
717         *-------------------------------------------------------------------*/
718        pArgString = argv[optind++]; /* set this to the next argument ptr */
719
720        if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */
721            ('-' != *pArgString)) {
722            --optind;               /* point to current arg once we're done */
723            optarg = NULL;          /* no argument follows the option */
724            pIndexPosition = NULL;  /* not in the middle of anything */
725            return EOF;             /* used up all the command-line flags */
726        }
727
728        /* check for special end-of-flags markers */
729        if ((strcmp(pArgString, "-") == 0) ||
730            (strcmp(pArgString, "--") == 0)) {
731            optarg = NULL;          /* no argument follows the option */
732            pIndexPosition = NULL;  /* not in the middle of anything */
733            return EOF;             /* encountered the special flag */
734        }
735
736        pArgString++;               /* look past the / or - */
737    }
738
739    if (':' == *pArgString) {       /* is it a colon? */
740        /*---------------------------------------------------------------------
741         * Rare case: if opterr is non-zero, return a question mark;
742         * otherwise, just return the colon we're on.
743         *-------------------------------------------------------------------*/
744        return (opterr ? (int)'?' : (int)':');
745    }
746    else if ((pOptString = strchr(opstring, *pArgString)) == 0) {
747        /*---------------------------------------------------------------------
748         * The letter on the command-line wasn't any good.
749         *-------------------------------------------------------------------*/
750        optarg = NULL;              /* no argument follows the option */
751        pIndexPosition = NULL;      /* not in the middle of anything */
752        return (opterr ? (int)'?' : (int)*pArgString);
753    }
754    else {
755        /*---------------------------------------------------------------------
756         * The letter on the command-line matches one we expect to see
757         *-------------------------------------------------------------------*/
758        if (':' == _next_char(pOptString)) { /* is the next letter a colon? */
759            /* It is a colon.  Look for an argument string. */
760            if ('\0' != _next_char(pArgString)) {  /* argument in this argv? */
761                optarg = &pArgString[1];   /* Yes, it is */
762            }
763            else {
764                /*-------------------------------------------------------------
765                 * The argument string must be in the next argv.
766                 * But, what if there is none (bad input from the user)?
767                 * In that case, return the letter, and optarg as NULL.
768                 *-----------------------------------------------------------*/
769                if (optind < argc)
770                    optarg = argv[optind++];
771                else {
772                    optarg = NULL;
773                    return (opterr ? (int)'?' : (int)*pArgString);
774                }
775            }
776            pIndexPosition = NULL;  /* not in the middle of anything */
777        }
778        else {
779            /* it's not a colon, so just return the letter */
780            optarg = NULL;          /* no argument follows the option */
781            pIndexPosition = pArgString;    /* point to the letter we're on */
782        }
783        return (int)*pArgString;    /* return the letter that matched */
784    }
785}
786
787#ifndef PASSWORD_MAX
788#  define PASSWORD_MAX 255
789#endif
790
791#include <conio.h>
792char *
793getpass(prompt)
794const char *prompt;
795{
796	register char *p;
797	register int c;
798	static char pbuf[PASSWORD_MAX];
799
800	fprintf(stderr, "%s", prompt); (void) fflush(stderr);
801	for (p=pbuf; (c = _getch())!=13 && c!=EOF;) {
802		if (p < &pbuf[sizeof(pbuf)-1])
803			*p++ = (char) c;
804	}
805	*p = '\0';
806	fprintf(stderr, "\n"); (void) fflush(stderr);
807	return(pbuf);
808}
809
810
811
812#endif /* WIN32 */
813