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