| /* |
| * encode_keychange.c |
| * |
| * Collect information to build a KeyChange encoding, per the textual |
| * convention given in RFC 2274, Section 5. Compute the value and |
| * dump to stdout as a string of hex nibbles. |
| * |
| * |
| * Passphrase material may come from many sources. The following are |
| * checked in order (see get_user_passphrases()): |
| * - Prompt always if -f is given. |
| * - Commandline arguments. |
| * - PASSPHRASE_FILE. |
| * - Prompts on stdout. Use -P to turn off prompt tags. |
| * |
| * |
| * FIX Better name? |
| * FIX Change encode_keychange() to take random bits? |
| * FIX QUITFUN not quite appropriate here... |
| * FIX This is slow... |
| */ |
| |
| #include <config.h> |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #ifdef HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| |
| #include "asn1.h" |
| #include "snmp_api.h" |
| #include "tools.h" |
| #include "scapi.h" |
| #include "snmpv3.h" |
| #include "keytools.h" |
| #include "snmp_debug.h" |
| #include "callback.h" |
| |
| #include "../snmplib/transform_oids.h" |
| |
| #include <stdlib.h> |
| |
| /* |
| * Globals, &c... |
| */ |
| char *local_progname; |
| |
| #define NL "\n" |
| |
| #define USAGE "Usage: %s [-fhPvV] -t (md5|sha1) [-O \"<old_passphrase>\"][-N \"<new_passphrase>\"][-E [0x]<engineID>]" |
| |
| #define OPTIONLIST "E:fhN:O:Pt:vVD" |
| |
| #define PASSPHRASE_DIR ".snmp" |
| /* Rooted at $HOME. |
| */ |
| #define PASSPHRASE_FILE PASSPHRASE_DIR ## "/passphrase.ek" |
| /* |
| * Format: two lines containing old and new passphrases, nothing more. |
| * |
| * XXX Add creature comforts like: comments and |
| * tokens identifying passphrases, separate directory check, |
| * check in current directory (?), traverse a path of |
| * directories (?)... |
| * FIX Better name? |
| */ |
| |
| |
| int forcepassphrase = 0, /* Always prompt for passphrases. */ |
| promptindicator = 1, /* Output an indicator that input |
| * is requested. */ |
| visible = 0, /* Echo passphrases to terminal. */ |
| verbose = 0; /* Output progress to stderr. */ |
| size_t engineid_len = 0; |
| |
| u_char *engineid = NULL; /* Both input & final binary form.*/ |
| char *newpass = NULL, |
| *oldpass = NULL; |
| |
| char *transform_type_input = NULL; |
| |
| oid *transform_type = NULL; /* Type of HMAC hash to use. */ |
| |
| |
| |
| /* |
| * Prototypes. |
| */ |
| void usage_to_file(FILE *ofp); |
| void usage_synopsis(FILE *ofp); |
| int get_user_passphrases(void); |
| int snmp_ttyecho(const int fd, const int echo); |
| char *snmp_getpassphrase(const char *prompt, int fvisible); |
| |
| #ifdef WIN32 |
| #define HAVE_GETPASS 1 |
| char *getpass( const char * prompt ); |
| int isatty(int); |
| int _cputs(const char *); |
| int _getch(void); |
| #endif |
| |
| /*******************************************************************-o-****** |
| */ |
| int |
| main(int argc, char **argv) |
| { |
| int rval = SNMPERR_SUCCESS; |
| size_t oldKu_len = SNMP_MAXBUF_SMALL, |
| newKu_len = SNMP_MAXBUF_SMALL, |
| oldkul_len = SNMP_MAXBUF_SMALL, |
| newkul_len = SNMP_MAXBUF_SMALL, |
| keychange_len = SNMP_MAXBUF_SMALL; |
| |
| char *s = NULL; |
| u_char oldKu[SNMP_MAXBUF_SMALL], |
| newKu[SNMP_MAXBUF_SMALL], |
| oldkul[SNMP_MAXBUF_SMALL], |
| newkul[SNMP_MAXBUF_SMALL], |
| keychange[SNMP_MAXBUF_SMALL]; |
| |
| int i; |
| int arg = 1; |
| |
| local_progname = argv[0]; |
| |
| |
| |
| /* |
| * Parse. |
| */ |
| for(; (arg < argc) && (argv[arg][0] == '-') ; arg++){ |
| switch(argv[arg][1]){ |
| case 'D': snmp_set_do_debugging(1); break; |
| case 'E': engineid = (u_char *)argv[++arg]; break; |
| case 'f': forcepassphrase = 1; break; |
| case 'N': newpass = argv[++arg]; break; |
| case 'O': oldpass = argv[++arg]; break; |
| case 'P': promptindicator = 0; break; |
| case 't': transform_type_input = argv[++arg]; break; |
| case 'v': verbose = 1; break; |
| case 'V': visible = 1; break; |
| case 'h': |
| rval = 0; |
| default: |
| usage_to_file(stdout); |
| exit(rval); |
| } |
| } |
| |
| if ( !transform_type_input ) { |
| fprintf(stderr, "The -t option is mandatory.\n"); |
| usage_synopsis(stdout); |
| exit(1000); |
| } |
| |
| |
| |
| /* |
| * Convert and error check transform_type. |
| */ |
| if ( !strcmp(transform_type_input, "md5") ) { |
| transform_type = usmHMACMD5AuthProtocol; |
| |
| } else if ( !strcmp(transform_type_input, "sha1") ) { |
| transform_type = usmHMACSHA1AuthProtocol; |
| |
| } else { |
| fprintf(stderr, |
| "Unrecognized hash transform: \"%s\".\n", |
| transform_type_input); |
| usage_synopsis(stderr); |
| QUITFUN(rval = SNMPERR_GENERR, main_quit); |
| } |
| |
| if (verbose) { |
| fprintf(stderr, "Hash:\t\t%s\n", |
| (transform_type == usmHMACMD5AuthProtocol) |
| ? "usmHMACMD5AuthProtocol" |
| : "usmHMACSHA1AuthProtocol" |
| ); |
| } |
| |
| |
| |
| /* |
| * Build engineID. Accept hex engineID as the bits |
| * "in-and-of-themselves", otherwise create an engineID with the |
| * given string as text. |
| * |
| * If no engineID is given, lookup the first IP address for the |
| * localhost and use that (see setup_engineID()). |
| */ |
| if ( engineid && (tolower(*(engineid+1)) == 'x') ) { |
| engineid_len = hex_to_binary2( engineid+2, |
| strlen((char *)engineid)-2, |
| (char **) &engineid); |
| DEBUGMSGTL(("encode_keychange","engineIDLen: %d\n", engineid_len)); |
| } else { |
| engineid_len = setup_engineID(&engineid, (char *)engineid); |
| |
| } |
| |
| #ifdef SNMP_TESTING_CODE |
| if (verbose) { |
| fprintf(stderr, "EngineID:\t%s\n", |
| /* XXX = */ dump_snmpEngineID(engineid, &engineid_len)); |
| } |
| #endif |
| |
| |
| /* |
| * Get passphrases from user. |
| */ |
| rval = get_user_passphrases(); |
| QUITFUN(rval, main_quit); |
| |
| if ( strlen(oldpass) < USM_LENGTH_P_MIN ) { |
| fprintf(stderr, "Old passphrase must be greater than %d " |
| "characters in length.\n", |
| USM_LENGTH_P_MIN); |
| QUITFUN(rval = SNMPERR_GENERR, main_quit); |
| |
| } else if ( strlen(newpass) < USM_LENGTH_P_MIN ) { |
| fprintf(stderr, "New passphrase must be greater than %d " |
| "characters in length.\n", |
| USM_LENGTH_P_MIN); |
| QUITFUN(rval = SNMPERR_GENERR, main_quit); |
| } |
| |
| if (verbose) { |
| fprintf(stderr, |
| "Old passphrase:\t%s\nNew passphrase:\t%s\n", |
| oldpass, newpass); |
| } |
| |
| |
| |
| /* |
| * Compute Ku and Kul's from old and new passphrases, then |
| * compute the keychange string & print it out. |
| */ |
| rval = sc_init(); |
| QUITFUN(rval, main_quit); |
| |
| |
| rval = generate_Ku( transform_type, USM_LENGTH_OID_TRANSFORM, |
| (u_char *)oldpass, strlen(oldpass), |
| oldKu, &oldKu_len); |
| QUITFUN(rval, main_quit); |
| |
| |
| rval = generate_Ku( transform_type, USM_LENGTH_OID_TRANSFORM, |
| (u_char *)newpass, strlen(newpass), |
| newKu, &newKu_len); |
| QUITFUN(rval, main_quit); |
| |
| |
| DEBUGMSGTL(("encode_keychange", "EID (%d): ", engineid_len)); |
| for(i=0; i < (int)engineid_len; i++) |
| DEBUGMSGTL(("encode_keychange", "%02x",(int) (engineid[i]))); |
| DEBUGMSGTL(("encode_keychange","\n")); |
| |
| DEBUGMSGTL(("encode_keychange", "old Ku (%d) (from %s): ", oldKu_len, oldpass)); |
| for(i=0; i < (int)oldKu_len; i++) |
| DEBUGMSGTL(("encode_keychange", "%02x",(int) (oldKu[i]))); |
| DEBUGMSGTL(("encode_keychange","\n")); |
| |
| rval = generate_kul( transform_type, USM_LENGTH_OID_TRANSFORM, |
| engineid, engineid_len, |
| oldKu, oldKu_len, |
| oldkul, &oldkul_len); |
| QUITFUN(rval, main_quit); |
| |
| |
| DEBUGMSGTL(("encode_keychange", "generating old Kul (%d) (from Ku): ", oldkul_len)); |
| for(i=0; i < (int)oldkul_len; i++) |
| DEBUGMSGTL(("encode_keychange", "%02x",(int) (oldkul[i]))); |
| DEBUGMSGTL(("encode_keychange","\n")); |
| |
| rval = generate_kul( transform_type, USM_LENGTH_OID_TRANSFORM, |
| engineid, engineid_len, |
| newKu, newKu_len, |
| newkul, &newkul_len); |
| QUITFUN(rval, main_quit); |
| |
| DEBUGMSGTL(("encode_keychange", "generating new Kul (%d) (from Ku): ", oldkul_len)); |
| for(i=0; i < (int)newkul_len; i++) |
| DEBUGMSGTL(("encode_keychange", "%02x",newkul[i])); |
| DEBUGMSGTL(("encode_keychange","\n")); |
| |
| rval = encode_keychange(transform_type, USM_LENGTH_OID_TRANSFORM, |
| oldkul, oldkul_len, |
| newkul, newkul_len, |
| keychange, &keychange_len); |
| QUITFUN(rval, main_quit); |
| |
| |
| |
| binary_to_hex(keychange, keychange_len, &s); |
| printf("%s%s\n", |
| (verbose) ? "KeyChange string:\t" : "", /* XXX stdout */ |
| s); |
| |
| |
| /* |
| * Cleanup. |
| */ |
| main_quit: |
| snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN, |
| NULL); |
| |
| |
| SNMP_ZERO(oldpass, strlen(oldpass)); |
| SNMP_ZERO(newpass, strlen(newpass)); |
| |
| SNMP_ZERO(oldKu, oldKu_len); |
| SNMP_ZERO(newKu, newKu_len); |
| |
| SNMP_ZERO(oldkul, oldkul_len); |
| SNMP_ZERO(newkul, newkul_len); |
| |
| SNMP_ZERO(s, strlen(s)); |
| |
| return rval; |
| |
| } /* end main() */ |
| |
| |
| |
| |
| /*******************************************************************-o-****** |
| */ |
| void |
| usage_synopsis(FILE *ofp) |
| { |
| fprintf(ofp, USAGE "\n\ |
| \n\ |
| -E [0x]<engineID> EngineID used for kul generation.\n\ |
| -f Force passphrases to be read from stdin.\n\ |
| -h Help.\n\ |
| -N \"<new_passphrase>\" Passphrase used to generate new Ku.\n\ |
| -O \"<old_passphrase>\" Passphrase used to generate old Ku.\n\ |
| -P Turn off prompt indicators.\n\ |
| -t md5 | sha1 HMAC hash transform type.\n\ |
| -v Verbose.\n\ |
| -V Visible. Echo passphrases to terminal.\n\ |
| " |
| NL, local_progname); |
| |
| } /* end usage_synopsis() */ |
| |
| void |
| usage_to_file(FILE *ofp) |
| { |
| char *s; |
| |
| usage_synopsis(ofp); |
| |
| fprintf(ofp, |
| "\n\ |
| Only -t is mandatory. The transform is used to convert P=>Ku, convert\n\ |
| Ku=>Kul, and to hash the old Kul with the random bits.\n\ |
| \n\ |
| Passphrase will be taken from the first successful source as follows:\n\ |
| a) Commandline options,\n\ |
| b) The file \"%s/%s\",\n\ |
| c) stdin -or- User input from the terminal.\n\ |
| \n\ |
| -f will require reading from the stdin/terminal, ignoring a) and b).\n\ |
| -P will prevent prompts for passphrases to stdout from being printed.\n\ |
| \n\ |
| <engineID> is intepreted as a hex string when preceeded by \"0x\",\n\ |
| otherwise it is created to contain \"text\". If nothing is given,\n\ |
| <engineID> is constructed from the first IP address for the local host.\n\ |
| " |
| NL, (s = getenv("HOME"))?s:"$HOME", PASSPHRASE_FILE); |
| |
| |
| /* FIX -- make this possible? |
| -r [0x]<random_bits> Random bits used in KeyChange XOR. |
| |
| <engineID> and <random_bits> are intepreted as hex strings when |
| preceeded by \"0x\", otherwise <engineID> is created to contain \"text\" |
| and <random_bits> are the same as the ascii input. |
| |
| <random_bits> will be generated by SCAPI if not given. If value is |
| too long, it will be truncated; if too short, the remainder will be |
| filled in with zeros. |
| */ |
| |
| } /* end usage() */ |
| |
| |
| /* this defined for HPUX aCC because the aCC doesn't drop the */ |
| /* snmp_parse_args.c functionality if compile with -g, PKY */ |
| |
| void usage(void) |
| { |
| usage_to_file(stdout); |
| } |
| |
| |
| |
| |
| |
| /*******************************************************************-o-****** |
| * get_user_passphrases |
| * |
| * Returns: |
| * SNMPERR_SUCCESS Success. |
| * SNMPERR_GENERR Otherwise. |
| * |
| * |
| * Acquire new and old passphrases from the user: |
| * |
| * + Always prompt if 'forcepassphrase' is set. |
| * + Use given arguments if they are defined. |
| * + Otherwise read file format from PASSWORD_FILE. |
| * Sanity check existence and permissions of the path. |
| * ASSUME for now that PASSWORD_FILE is rooted only at $HOME. |
| * + Otherwise prompt user for passphrase(s). |
| * Echo input if 'visible' is set. |
| * Turning off 'promptindicator' makes piping in input cleaner. |
| * |
| * NOTE Only using forcepassphrase mandates taking both passphrases |
| * from the same source. Otherwise processing continues until both |
| * passphrases are defined. |
| */ |
| int |
| get_user_passphrases(void) |
| { |
| int rval = SNMPERR_SUCCESS; |
| size_t len; |
| |
| char *obuf = NULL, |
| *nbuf = NULL; |
| |
| char path[SNMP_MAXBUF], |
| buf[SNMP_MAXBUF], |
| *s = NULL; |
| |
| struct stat statbuf; |
| FILE *fp; |
| |
| |
| |
| /* |
| * Allow prompts to the user to override all other sources. |
| * Nothing to do otherwise if oldpass and newpass are already defined. |
| */ |
| if ( forcepassphrase ) goto get_user_passphrases_prompt; |
| if ( oldpass && newpass ) goto get_user_passphrases_quit; |
| |
| |
| |
| /* |
| * Read passphrases out of PASSPHRASE_FILE. Sanity check the |
| * path for existence and access first. Refuse to read |
| * if the permissions are wrong. |
| */ |
| s = getenv("HOME"); |
| sprintf(path, "%s/%s", s, PASSPHRASE_DIR); |
| |
| /* Test directory. */ |
| if ( stat(path, &statbuf) < 0 ) { |
| fprintf(stderr, "Cannot access directory \"%s\".\n", path); |
| QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit); |
| #ifndef WIN32 |
| } else if ( statbuf.st_mode & (S_IRWXG|S_IRWXO) ) { |
| fprintf(stderr, |
| "Directory \"%s\" is accessible by group or world.\n", |
| path); |
| QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit); |
| #endif /* !WIN32 */ |
| } |
| |
| /* Test file. */ |
| sprintf(path, "%s/%s", s, PASSPHRASE_FILE); |
| if ( stat(path, &statbuf) < 0 ) { |
| fprintf(stderr, "Cannot access file \"%s\".\n", path); |
| QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit); |
| #ifndef WIN32 |
| } else if ( statbuf.st_mode & (S_IRWXG|S_IRWXO) ) { |
| fprintf(stderr, |
| "File \"%s\" is accessible by group or world.\n", path); |
| QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit); |
| #endif /* !WIN32 */ |
| } |
| |
| /* Open the file. */ |
| if ( (fp = fopen(path, "r")) == NULL ) { |
| fprintf(stderr, "Cannot open \"%s\".", path); |
| QUITFUN(rval = SNMPERR_GENERR, get_user_passphrases_quit); |
| } |
| |
| /* Read 1st line. */ |
| if ( !fgets(buf, sizeof(buf), fp) ) { |
| if ( verbose ) { |
| fprintf(stderr, |
| "Passphrase file \"%s\" is empty...\n", path); |
| } |
| goto get_user_passphrases_prompt; |
| |
| } else if ( !oldpass ) { |
| len = strlen(buf); |
| if ( buf[len-1] == '\n' ) buf[--len] = '\0'; |
| oldpass = (char *)calloc(1,len+1); |
| memcpy(oldpass, buf, len+1); |
| } |
| /* Read 2nd line. */ |
| if ( !fgets(buf, sizeof(buf), fp) ) { |
| if ( verbose ) { |
| fprintf(stderr, |
| "Only one line in file \"%s\"...\n", path); |
| } |
| |
| } else if ( !newpass ) { |
| len = strlen(buf); |
| if ( buf[len-1] == '\n' ) buf[--len] = '\0'; |
| newpass = (char *)calloc(1,len+1); |
| memcpy(newpass, buf, len+1); |
| } |
| |
| if ( oldpass && newpass ) goto get_user_passphrases_quit; |
| |
| |
| |
| /* |
| * Prompt the user for passphrase entry. Visible prompts |
| * may be omitted, and invisible entry may turned off. |
| */ |
| get_user_passphrases_prompt: |
| if ( forcepassphrase ) { |
| oldpass = newpass = NULL; |
| } |
| |
| if ( ! oldpass ) { |
| oldpass = obuf |
| = snmp_getpassphrase( |
| (promptindicator) ? "Old passphrase: " : "", |
| visible); |
| } |
| if ( ! newpass ) { |
| newpass = nbuf |
| = snmp_getpassphrase( |
| (promptindicator) ? "New passphrase: " : "", |
| visible); |
| } |
| |
| |
| |
| /* |
| * Check that both passphrases were defined. |
| */ |
| if ( oldpass && newpass ) { |
| goto get_user_passphrases_quit; |
| } else { |
| rval = SNMPERR_GENERR; |
| } |
| |
| |
| get_user_passphrases_quit: |
| SNMP_ZERO(buf, SNMP_MAXBUF); |
| |
| if ( obuf != oldpass ) { |
| SNMP_ZERO(obuf, strlen(obuf)); |
| SNMP_FREE(obuf); |
| } |
| if ( nbuf != newpass ) { |
| SNMP_ZERO(nbuf, strlen(nbuf)); |
| SNMP_FREE(nbuf); |
| } |
| |
| return rval; |
| |
| } /* end get_user_passphrases() */ |
| |
| /*******************************************************************-o-****** |
| * snmp_ttyecho |
| * |
| * Parameters: |
| * fd Descriptor of terminal on which to toggle echoing. |
| * echo TRUE if echoing should be on; FALSE otherwise. |
| * |
| * Returns: |
| * Previous value of echo setting. |
| * |
| * |
| * FIX Put HAVE_TCGETATTR in autoconf? |
| */ |
| #ifndef HAVE_GETPASS |
| #ifdef HAVE_TCGETATTR |
| #include <termios.h> |
| int |
| snmp_ttyecho(const int fd, const int echo) |
| { |
| struct termios tio; |
| int was_echo; |
| |
| |
| if (!isatty(fd)) |
| return (-1); |
| tcgetattr(fd, &tio); |
| was_echo = (tio.c_lflag & ECHO) != 0; |
| if (echo) |
| tio.c_lflag |= (ECHO | ECHONL); |
| else |
| tio.c_lflag &= ~(ECHO | ECHONL); |
| tcsetattr(fd, TCSANOW, &tio); |
| |
| return (was_echo); |
| |
| } /* end snmp_ttyecho() */ |
| |
| #else |
| #include <sgtty.h> |
| int |
| snmp_ttyecho(const int fd, const int echo) |
| { |
| struct sgttyb ttyparams; |
| int was_echo; |
| |
| |
| if (!isatty(fd)) |
| was_echo = -1; |
| else { |
| ioctl(fd, TIOCGETP, &ttyparams); |
| was_echo = (ttyparams.sg_flags & ECHO) != 0; |
| if (echo) |
| ttyparams.sg_flags = ttyparams.sg_flags | ECHO; |
| else |
| ttyparams.sg_flags = ttyparams.sg_flags & ~ECHO; |
| ioctl(fd, TIOCSETP, &ttyparams); |
| } |
| |
| return (was_echo); |
| |
| } /* end snmp_ttyecho() */ |
| #endif /* HAVE_TCGETATTR */ |
| #endif /* HAVE_GETPASS */ |
| |
| |
| |
| |
| /*******************************************************************-o-****** |
| * snmp_getpassphrase |
| * |
| * Parameters: |
| * *prompt (May be NULL.) |
| * bvisible TRUE means echo back user input. |
| * |
| * Returns: |
| * Pointer to newly allocated, null terminated string containing |
| * passphrase -OR- |
| * NULL on error. |
| * |
| * |
| * Prompt stdin for a string (or passphrase). Return a copy of the |
| * input in a null terminated string. |
| * |
| * FIX Put HAVE_GETPASS in autoconf. |
| */ |
| char * |
| snmp_getpassphrase(const char *prompt, int bvisible) |
| { |
| int ti = 0; |
| size_t len; |
| |
| char *bufp = NULL; |
| static char buffer[SNMP_MAXBUF]; |
| |
| FILE *ofp = stdout; |
| |
| |
| /* |
| * Query stdin for a passphrase. |
| */ |
| #ifdef HAVE_GETPASS |
| if ( isatty(0) ) { |
| return getpass( (prompt) ? prompt : "" ); |
| } |
| #endif |
| |
| fputs( (prompt) ? prompt : "", ofp ); |
| |
| if ( !bvisible ) { |
| ti = snmp_ttyecho(0, 0); |
| } |
| |
| fgets(buffer, sizeof(buffer), stdin); |
| |
| if ( !bvisible ) { |
| ti = snmp_ttyecho(0, ti); |
| fputs( "\n", ofp ); |
| } |
| |
| |
| /* |
| * Copy the input and zero out the read-in buffer. |
| */ |
| len = strlen(buffer); |
| if ( buffer[len-1] == '\n' ) buffer[--len] = '\0'; |
| |
| bufp = (char *)calloc(1,len+1); |
| memcpy(bufp, buffer, len+1); |
| |
| SNMP_ZERO(buffer, SNMP_MAXBUF); |
| |
| |
| return bufp; |
| |
| } /* end snmp_getpassphrase() */ |
| |
| #ifdef WIN32 |
| |
| int snmp_ttyecho(const int fd, const int echo) { return 0; } |
| |
| /* |
| * stops at the first newline, carrier return, or backspace. |
| * WARNING! _getch does NOT read <Ctrl-C> |
| */ |
| char *getpass( const char * prompt ) |
| { |
| static char pbuf[128]; |
| int ch, lim; |
| |
| _cputs(prompt); |
| for (ch=0, lim=0; ch != '\n' && lim < sizeof(pbuf); ) |
| { |
| ch = _getch(); /* look ma, no echo ! */ |
| if (ch == '\r' || ch == '\n' || ch == '\b') |
| break; |
| pbuf[lim++] = ch; |
| } |
| pbuf[lim] = '\0'; |
| puts("\n"); |
| |
| return pbuf; |
| } |
| #endif /* WIN32 */ |
| |