GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / lib / saslutil.c
1 /* saslutil.c
2  * Rob Siemborski
3  * Tim Martin
4  * $Id: saslutil.c,v 1.44.2.1 2009/04/27 17:47:17 murch 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
77 char *encode_table;
78 char *decode_table;
79
80 #define RPOOL_SIZE 3
81 struct 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
89 static char basis_64[] =
90    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";
91
92 static 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
114 int 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_CONTINUE on a partial block,
173  * SASL_OK on success
174  */
175
176 int sasl_decode64(const char *in,
177                   unsigned inlen,
178                   char *out,
179                   unsigned outmax,  /* size of the buffer, not counting the NUL */
180                   unsigned *outlen)
181 {
182     unsigned len = 0;
183     unsigned j;
184     int c[4];
185     int saw_equal = 0;
186
187     /* check parameters */
188     if (out == NULL) return SASL_FAIL;
189
190     if (inlen > 0 && *in == '\r') return SASL_FAIL;
191
192     while (inlen > 3) {
193         /* No data is valid after an '=' character */
194         if (saw_equal) {
195             return SASL_BADPROT;
196         }
197
198         for (j = 0; j < 4; j++) {
199             c[j] = in[0];
200             in++;
201             inlen--;
202         }
203
204         if (CHAR64(c[0]) == -1 || CHAR64(c[1]) == -1) return SASL_BADPROT;
205         if (c[2] != '=' && CHAR64(c[2]) == -1) return SASL_BADPROT;
206         if (c[3] != '=' && CHAR64(c[3]) == -1) return SASL_BADPROT;
207         /* No data is valid after a '=' character, unless it is another '=' */
208         if (c[2] == '=' && c[3] != '=') return SASL_BADPROT;
209         if (c[2] == '=' || c[3] == '=') {
210             saw_equal = 1;
211         }
212
213         *out++ = (CHAR64(c[0]) << 2) | (CHAR64(c[1]) >> 4);
214         if (++len >= outmax) return SASL_BUFOVER;
215         if (c[2] != '=') {
216             *out++ = ((CHAR64(c[1]) << 4) & 0xf0) | (CHAR64(c[2]) >> 2);
217             if (++len >= outmax) return SASL_BUFOVER;
218             if (c[3] != '=') {
219                 *out++ = ((CHAR64(c[2]) << 6) & 0xc0) | CHAR64(c[3]);
220                 if (++len >= outmax) return SASL_BUFOVER;
221             }
222         }
223     }
224
225     if (inlen != 0) {
226         if (saw_equal) {
227             /* Unless there is CRLF at the end? */
228             return SASL_BADPROT;
229         } else {
230             return (SASL_CONTINUE);
231         }
232     }
233
234     *out = '\0'; /* NUL terminate the output string */
235
236     if (outlen) *outlen = len;
237
238     return SASL_OK;
239 }
240
241 /* make a challenge string (NUL terminated)
242  *  buf      -- buffer for result
243  *  maxlen   -- max length of result
244  *  hostflag -- 0 = don't include hostname, 1 = include hostname
245  * returns final length or 0 if not enough space
246  */
247
248 int sasl_mkchal(sasl_conn_t *conn,
249                 char *buf,
250                 unsigned maxlen,
251                 unsigned hostflag)
252 {
253   sasl_rand_t *pool = NULL;
254   unsigned long randnum;
255   int ret;
256   time_t now;
257   unsigned len;
258
259   len = 4                       /* <.>\0 */
260     + (2 * 20);                 /* 2 numbers, 20 => max size of 64bit
261                                  * ulong in base 10 */
262   if (hostflag && conn->serverFQDN)
263     len += (unsigned) strlen(conn->serverFQDN) + 1 /* for the @ */;
264
265   if (maxlen < len)
266     return 0;
267
268   ret = sasl_randcreate(&pool);
269   if(ret != SASL_OK) return 0; /* xxx sasl return code? */
270
271   sasl_rand(pool, (char *)&randnum, sizeof(randnum));
272   sasl_randfree(&pool);
273
274   time(&now);
275
276   if (hostflag && conn->serverFQDN)
277     snprintf(buf,maxlen, "<%lu.%lu@%s>", randnum, now, conn->serverFQDN);
278   else
279     snprintf(buf,maxlen, "<%lu.%lu>", randnum, now);
280
281   return (int) strlen(buf);
282 }
283
284   /* borrowed from larry. probably works :)
285    * probably is also in acap server somewhere
286    */
287 int sasl_utf8verify(const char *str, unsigned len)
288 {
289   unsigned i;
290   for (i = 0; i < len; i++) {
291     /* how many octets? */
292     int seqlen = 0;
293     while (str[i] & (0x80 >> seqlen)) ++seqlen;
294     if (seqlen == 0) continue; /* this is a valid US-ASCII char */
295     if (seqlen == 1) return SASL_BADPROT; /* this shouldn't happen here */
296     if (seqlen > 6) return SASL_BADPROT; /* illegal */
297     while (--seqlen)
298       if ((str[++i] & 0xC0) != 0xF0) return SASL_BADPROT; /* needed a 10 octet */
299   }
300   return SASL_OK;
301 }      
302
303 /* 
304  * To see why this is really bad see RFC 1750
305  *
306  * unfortunatly there currently is no way to make 
307  * cryptographically secure pseudo random numbers
308  * without specialized hardware etc...
309  * thus, this is for nonce use only
310  */
311 void getranddata(unsigned short ret[RPOOL_SIZE])
312 {
313     long curtime;
314     
315     memset(ret, 0, RPOOL_SIZE*sizeof(unsigned short));
316
317 #ifdef DEV_RANDOM    
318     {
319         int fd;
320
321         fd = open(DEV_RANDOM, O_RDONLY);
322         if(fd != -1) {
323             unsigned char *buf = (unsigned char *)ret;
324             ssize_t bytesread = 0;
325             size_t bytesleft = RPOOL_SIZE*sizeof(unsigned short);
326             
327             do {
328                 bytesread = read(fd, buf, bytesleft);
329                 if(bytesread == -1 && errno == EINTR) continue;
330                 else if(bytesread <= 0) break;
331                 bytesleft -= bytesread;
332                 buf += bytesread;
333             } while(bytesleft != 0);
334                 
335             close(fd);
336         }
337     }
338 #endif
339
340 #ifdef HAVE_GETPID
341     ret[0] ^= (unsigned short) getpid();
342 #endif
343
344 #ifdef HAVE_GETTIMEOFDAY
345     {
346         struct timeval tv;
347         
348         /* xxx autoconf macro */
349 #ifdef _SVID_GETTOD
350         if (!gettimeofday(&tv))
351 #else
352         if (!gettimeofday(&tv, NULL))
353 #endif
354         {
355             /* longs are guaranteed to be at least 32 bits; we need
356                16 bits in each short */
357             ret[0] ^= (unsigned short) (tv.tv_sec & 0xFFFF);
358             ret[1] ^= (unsigned short) (clock() & 0xFFFF);
359             ret[1] ^= (unsigned short) (tv.tv_usec >> 16);
360             ret[2] ^= (unsigned short) (tv.tv_usec & 0xFFFF);
361             return;
362         }
363     }
364 #endif /* HAVE_GETTIMEOFDAY */
365     
366     /* if all else fails just use time() */
367     curtime = (long) time(NULL); /* better be at least 32 bits */
368     
369     ret[0] ^= (unsigned short) (curtime >> 16);
370     ret[1] ^= (unsigned short) (curtime & 0xFFFF);
371     ret[2] ^= (unsigned short) (clock() & 0xFFFF);
372     
373     return;
374 }
375
376 int sasl_randcreate(sasl_rand_t **rpool)
377 {
378   (*rpool)=sasl_ALLOC(sizeof(sasl_rand_t));
379   if ((*rpool) == NULL) return SASL_NOMEM;
380
381   /* init is lazy */
382   (*rpool)->initialized = 0;
383
384   return SASL_OK;
385 }
386
387 void sasl_randfree(sasl_rand_t **rpool)
388 {
389     sasl_FREE(*rpool);
390 }
391
392 void sasl_randseed (sasl_rand_t *rpool, const char *seed, unsigned len)
393 {
394     /* is it acceptable to just use the 1st 3 char's given??? */
395     unsigned int lup;
396
397     /* check params */
398     if (seed == NULL) return;
399     if (rpool == NULL) return;
400
401     rpool->initialized = 1;
402
403     if (len > sizeof(unsigned short)*RPOOL_SIZE)
404       len = sizeof(unsigned short)*RPOOL_SIZE;
405
406     for (lup = 0; lup < len; lup += 2)
407         rpool->pool[lup/2] = (seed[lup] << 8) + seed[lup + 1];
408 }
409
410 static void randinit(sasl_rand_t *rpool)
411 {
412     if (!rpool) return;
413     
414     if (!rpool->initialized) {
415         getranddata(rpool->pool);
416         rpool->initialized = 1;
417 #if !(defined(WIN32)||defined(macintosh))
418 #ifndef HAVE_JRAND48
419     {
420       /* xxx varies by platform */
421         unsigned int *foo = (unsigned int *)rpool->pool;
422         srandom(*foo);
423     }
424 #endif /* HAVE_JRAND48 */
425 #endif /* WIN32 */
426     }
427
428 }
429
430 void sasl_rand (sasl_rand_t *rpool, char *buf, unsigned len)
431 {
432     unsigned int lup;
433     /* check params */
434     if (!rpool || !buf) return;
435     
436     /* init if necessary */
437     randinit(rpool);
438  
439 #if (defined(WIN32)||defined(macintosh))
440     for (lup=0;lup<len;lup++)
441         buf[lup] = (char) (rand() >> 8);
442 #else /* WIN32 */
443 #ifdef HAVE_JRAND48
444     for (lup=0; lup<len; lup++)
445         buf[lup] = (char) (jrand48(rpool->pool) >> 8);
446 #else
447     for (lup=0;lup<len;lup++)
448         buf[lup] = (char) (random() >> 8);
449 #endif /* HAVE_JRAND48 */
450 #endif /* WIN32 */
451 }
452
453 /* this function is just a bad idea all around, since we're not trying to
454    implement a true random number generator */
455 void sasl_churn (sasl_rand_t *rpool, const char *data, unsigned len)
456 {
457     unsigned int lup;
458     
459     /* check params */
460     if (!rpool || !data) return;
461     
462     /* init if necessary */
463     randinit(rpool);
464     
465     for (lup=0; lup<len; lup++)
466         rpool->pool[lup % RPOOL_SIZE] ^= data[lup];
467 }
468
469 void sasl_erasebuffer(char *buf, unsigned len) {
470     memset(buf, 0, len);
471 }
472
473 #ifdef WIN32
474 /***************************************************************************** 
475  * 
476  *  MODULE NAME : GETOPT.C 
477  * 
478  *  COPYRIGHTS: 
479  *             This module contains code made available by IBM 
480  *             Corporation on an AS IS basis.  Any one receiving the 
481  *             module is considered to be licensed under IBM copyrights 
482  *             to use the IBM-provided source code in any way he or she 
483  *             deems fit, including copying it, compiling it, modifying 
484  *             it, and redistributing it, with or without 
485  *             modifications.  No license under any IBM patents or 
486  *             patent applications is to be implied from this copyright 
487  *             license. 
488  * 
489  *             A user of the module should understand that IBM cannot 
490  *             provide technical support for the module and will not be 
491  *             responsible for any consequences of use of the program. 
492  * 
493  *             Any notices, including this one, are not to be removed 
494  *             from the module without the prior written consent of 
495  *             IBM. 
496  * 
497  *  AUTHOR:   Original author: 
498  *                 G. R. Blair (BOBBLAIR at AUSVM1) 
499  *                 Internet: bobblair@bobblair.austin.ibm.com 
500  * 
501  *            Extensively revised by: 
502  *                 John Q. Walker II, Ph.D. (JOHHQ at RALVM6) 
503  *                 Internet: johnq@ralvm6.vnet.ibm.com 
504  * 
505  *****************************************************************************/ 
506  
507 /****************************************************************************** 
508  * getopt() 
509  * 
510  * The getopt() function is a command line parser.  It returns the next 
511  * option character in argv that matches an option character in opstring. 
512  * 
513  * The argv argument points to an array of argc+1 elements containing argc 
514  * pointers to character strings followed by a null pointer. 
515  * 
516  * The opstring argument points to a string of option characters; if an 
517  * option character is followed by a colon, the option is expected to have 
518  * an argument that may or may not be separated from it by white space. 
519  * The external variable optarg is set to point to the start of the option 
520  * argument on return from getopt(). 
521  * 
522  * The getopt() function places in optind the argv index of the next argument 
523  * to be processed.  The system initializes the external variable optind to 
524  * 1 before the first call to getopt(). 
525  * 
526  * When all options have been processed (that is, up to the first nonoption 
527  * argument), getopt() returns EOF.  The special option "--" may be used to 
528  * delimit the end of the options; EOF will be returned, and "--" will be 
529  * skipped. 
530  * 
531  * The getopt() function returns a question mark (?) when it encounters an 
532  * option character not included in opstring.  This error message can be 
533  * disabled by setting opterr to zero.  Otherwise, it returns the option 
534  * character that was detected. 
535  * 
536  * If the special option "--" is detected, or all options have been 
537  * processed, EOF is returned. 
538  * 
539  * Options are marked by either a minus sign (-) or a slash (/). 
540  * 
541  * No errors are defined. 
542  *****************************************************************************/ 
543  
544 #include <string.h>                 /* for strchr() */ 
545  
546 /* static (global) variables that are specified as exported by getopt() */ 
547 __declspec(dllexport) char *optarg = NULL;    /* pointer to the start of the option argument  */ 
548 __declspec(dllexport) int   optind = 1;       /* number of the next argv[] to be evaluated    */ 
549 __declspec(dllexport) int   opterr = 1;       /* non-zero if a question mark should be returned */
550
551  
552 /* handle possible future character set concerns by putting this in a macro */ 
553 #define _next_char(string)  (char)(*(string+1)) 
554  
555 int getopt(int argc, char *argv[], char *opstring) 
556
557     static char *pIndexPosition = NULL; /* place inside current argv string */ 
558     char *pArgString = NULL;        /* where to start from next */ 
559     char *pOptString;               /* the string in our program */ 
560  
561  
562     if (pIndexPosition != NULL) { 
563         /* we last left off inside an argv string */ 
564         if (*(++pIndexPosition)) { 
565             /* there is more to come in the most recent argv */ 
566             pArgString = pIndexPosition; 
567         } 
568     } 
569  
570     if (pArgString == NULL) { 
571         /* we didn't leave off in the middle of an argv string */ 
572         if (optind >= argc) { 
573             /* more command-line arguments than the argument count */ 
574             pIndexPosition = NULL;  /* not in the middle of anything */ 
575             return EOF;             /* used up all command-line arguments */ 
576         } 
577  
578         /*--------------------------------------------------------------------- 
579          * If the next argv[] is not an option, there can be no more options. 
580          *-------------------------------------------------------------------*/ 
581         pArgString = argv[optind++]; /* set this to the next argument ptr */ 
582  
583         if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ 
584             ('-' != *pArgString)) { 
585             --optind;               /* point to current arg once we're done */ 
586             optarg = NULL;          /* no argument follows the option */ 
587             pIndexPosition = NULL;  /* not in the middle of anything */ 
588             return EOF;             /* used up all the command-line flags */ 
589         } 
590  
591         /* check for special end-of-flags markers */ 
592         if ((strcmp(pArgString, "-") == 0) || 
593             (strcmp(pArgString, "--") == 0)) { 
594             optarg = NULL;          /* no argument follows the option */ 
595             pIndexPosition = NULL;  /* not in the middle of anything */ 
596             return EOF;             /* encountered the special flag */ 
597         } 
598  
599         pArgString++;               /* look past the / or - */ 
600     } 
601  
602     if (':' == *pArgString) {       /* is it a colon? */ 
603         /*--------------------------------------------------------------------- 
604          * Rare case: if opterr is non-zero, return a question mark; 
605          * otherwise, just return the colon we're on. 
606          *-------------------------------------------------------------------*/ 
607         return (opterr ? (int)'?' : (int)':'); 
608     } 
609     else if ((pOptString = strchr(opstring, *pArgString)) == 0) { 
610         /*--------------------------------------------------------------------- 
611          * The letter on the command-line wasn't any good. 
612          *-------------------------------------------------------------------*/ 
613         optarg = NULL;              /* no argument follows the option */ 
614         pIndexPosition = NULL;      /* not in the middle of anything */ 
615         return (opterr ? (int)'?' : (int)*pArgString); 
616     } 
617     else { 
618         /*--------------------------------------------------------------------- 
619          * The letter on the command-line matches one we expect to see 
620          *-------------------------------------------------------------------*/ 
621         if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ 
622             /* It is a colon.  Look for an argument string. */ 
623             if ('\0' != _next_char(pArgString)) {  /* argument in this argv? */ 
624                 optarg = &pArgString[1];   /* Yes, it is */ 
625             } 
626             else { 
627                 /*------------------------------------------------------------- 
628                  * The argument string must be in the next argv. 
629                  * But, what if there is none (bad input from the user)? 
630                  * In that case, return the letter, and optarg as NULL. 
631                  *-----------------------------------------------------------*/ 
632                 if (optind < argc) 
633                     optarg = argv[optind++]; 
634                 else { 
635                     optarg = NULL; 
636                     return (opterr ? (int)'?' : (int)*pArgString); 
637                 } 
638             } 
639             pIndexPosition = NULL;  /* not in the middle of anything */ 
640         } 
641         else { 
642             /* it's not a colon, so just return the letter */ 
643             optarg = NULL;          /* no argument follows the option */ 
644             pIndexPosition = pArgString;    /* point to the letter we're on */ 
645         } 
646         return (int)*pArgString;    /* return the letter that matched */ 
647     } 
648
649
650 #ifndef PASSWORD_MAX
651 #  define PASSWORD_MAX 255
652 #endif
653
654 #include <conio.h>
655 char *
656 getpass(prompt)
657 const char *prompt;
658 {
659         register char *p;
660         register c;
661         static char pbuf[PASSWORD_MAX];
662
663         fprintf(stderr, "%s", prompt); (void) fflush(stderr);
664         for (p=pbuf; (c = _getch())!=13 && c!=EOF;) {
665                 if (p < &pbuf[sizeof(pbuf)-1])
666                         *p++ = c;
667         }
668         *p = '\0';
669         fprintf(stderr, "\n"); (void) fflush(stderr);
670         return(pbuf);
671 }
672
673
674
675 #endif /* WIN32 */