diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index c9df939e..cf637748 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -216,7 +216,7 @@ void publickey_free(PUBLIC_KEY *key); /* in keyfiles.c */ -PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase); +PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,const char *passphrase); STRING *publickey_to_string(PUBLIC_KEY *key); PUBLIC_KEY *publickey_from_privatekey(PRIVATE_KEY *prv); void private_key_free(PRIVATE_KEY *prv); @@ -253,6 +253,22 @@ int channel_select(CHANNEL **readchans, CHANNEL **writechans, CHANNEL **exceptch timeval * timeout); /* in options.c */ +/** + * @brief SSH authentication callback. + * + * @param prompt Prompt to be displayed. + * @param buf Buffer to save the password. You should null-terminate it. + * @param len Length of the buffer. + * @param echo Enable or disable the echo of what you type. + * @param verify Should the password be verified? + * @param userdata Userdata to be passed to the callback function. Useful + * for GUI applications. + * + * @return 0 on success, < 0 on error. + */ +typedef int (*ssh_auth_callback) (const char *prompt, char *buf, size_t len, + int echo, int verify, void *userdata); + SSH_OPTIONS *ssh_options_new(); SSH_OPTIONS *ssh_options_copy(SSH_OPTIONS *opt); int ssh_options_set_wanted_algos(SSH_OPTIONS *opt, int algo, const char *list); @@ -276,6 +292,17 @@ void ssh_options_set_log_function(SSH_OPTIONS *opt, void (*callback)(const char *message, SSH_SESSION *session, int verbosity )); void ssh_options_set_log_verbosity(SSH_OPTIONS *opt, int verbosity); +/** + * @brief Set the authentication callback. + * + * @param opt The options structure to use. + * @param cb The callback function to use. + * @param userdata A pointer to some user data you can pass to the + * callback. + */ +void ssh_options_set_auth_callback(SSH_OPTIONS *opt, ssh_auth_callback cb, + void *userdata); + /* buffer.c */ @@ -299,7 +326,7 @@ int ssh_userauth_none(SSH_SESSION *session, const char *username); int ssh_userauth_password(SSH_SESSION *session, const char *username, const char *password); int ssh_userauth_offer_pubkey(SSH_SESSION *session, const char *username, int type, STRING *publickey); int ssh_userauth_pubkey(SSH_SESSION *session, const char *username, STRING *publickey, PRIVATE_KEY *privatekey); -int ssh_userauth_autopubkey(SSH_SESSION *session); +int ssh_userauth_autopubkey(SSH_SESSION *session, const char *passphrase); int ssh_userauth_kbdint(SSH_SESSION *session, const char *user, const char *submethods); int ssh_userauth_kbdint_getnprompts(SSH_SESSION *session); char *ssh_userauth_kbdint_getname(SSH_SESSION *session); diff --git a/include/libssh/priv.h b/include/libssh/priv.h index c7afb423..34731a69 100644 --- a/include/libssh/priv.h +++ b/include/libssh/priv.h @@ -228,7 +228,8 @@ struct ssh_options_struct { int use_nonexisting_algo; /* if user sets a not supported algorithm for kex, don't complain */ char *wanted_methods[10]; /* the kex methods can be choosed. better use the kex fonctions to do that */ void *wanted_cookie; /* wants a specific cookie to be sent ? if null, generate a new one */ - void *passphrase_function; /* this functions will be called if a keyphrase is needed. look keyfiles.c for more info */ + ssh_auth_callback auth_function; /* this functions will be called if e.g. a keyphrase is needed. */ + void *auth_userdata; void (*connect_status_function)(void *arg, float status); /* status callback function */ void *connect_status_arg; /* arbitrary argument */ long timeout; /* seconds */ diff --git a/libssh/auth.c b/libssh/auth.c index a0707500..eb212f75 100644 --- a/libssh/auth.c +++ b/libssh/auth.c @@ -405,6 +405,9 @@ static char *pub_keys_path[]={NULL,"%s/.ssh/identity.pub","%s/.ssh/id_dsa.pub"," * asker for passphrases (in case the private key is encrypted) * \brief Tries to automaticaly authenticate with public key and "none" * \param session ssh session + * \param passphrase use this passphrase to unlock the privatekey. Use + * NULL if you don't want to use a passphrase or the + * user should be asked. * \returns SSH_AUTH_ERROR : a serious error happened\n * SSH_AUTH_DENIED : Authentication failed : use another method\n * SSH_AUTH_PARTIAL : You've been partially authenticated, you still have to use another method\n @@ -414,7 +417,7 @@ static char *pub_keys_path[]={NULL,"%s/.ssh/identity.pub","%s/.ssh/id_dsa.pub"," * \see ssh_options_set_identity() */ -int ssh_userauth_autopubkey(SSH_SESSION *session){ +int ssh_userauth_autopubkey(SSH_SESSION *session, const char *passphrase) { int count=1; /* bypass identity */ int type=0; int err; @@ -460,7 +463,7 @@ int ssh_userauth_autopubkey(SSH_SESSION *session){ continue; } /* pubkey accepted by server ! */ - privkey=privatekey_from_file(session,privkeyfile,type,NULL); + privkey=privatekey_from_file(session,privkeyfile,type,passphrase); if(!privkey){ ssh_say(0,"Reading private key %s failed (bad passphrase ?)\n",privkeyfile); free(pubkey); diff --git a/libssh/keyfiles.c b/libssh/keyfiles.c index a0b71a40..d1bb9197 100644 --- a/libssh/keyfiles.c +++ b/libssh/keyfiles.c @@ -192,22 +192,29 @@ int passphrase_to_key(char *data, unsigned int datalen, unsigned char *salt, uns int privatekey_decrypt(int algo, int mode, unsigned int key_len, unsigned char *iv, unsigned int iv_len, - BUFFER *data, int cb(char *,int , int , char *), + BUFFER *data, ssh_auth_callback cb, + void *userdata, char *desc) { gcry_cipher_hd_t cipher; - unsigned int passphrase_len; - char passphrase[MAX_PASSPHRASE_SIZE]; - unsigned char key[MAX_KEY_SIZE]; + int rc = -1; + char passphrase[MAX_PASSPHRASE_SIZE] = {0}; + unsigned char key[MAX_KEY_SIZE] = {0}; unsigned char *tmp; gcry_error_t err; if (!algo) return 1; - passphrase_len=cb(passphrase, MAX_PASSPHRASE_SIZE, 0, desc); - if (passphrase_len <= 0) - return 0; - passphrase_to_key(passphrase, passphrase_len, iv, key, key_len); + + if (cb) { + rc = (*cb)(desc, passphrase, MAX_PASSPHRASE_SIZE, 0, 0, userdata); + if (rc < 0) { + return 0; + } + } else if (cb == NULL && userdata != NULL) { + sprintf(buf, MAX_PASSPHRASE_SIZE, "%s", userdata); + } + passphrase_to_key(passphrase, strlen(passphrase), iv, key, key_len); if (gcry_cipher_open(&cipher, algo, mode, 0) || gcry_cipher_setkey(cipher, key, key_len) || gcry_cipher_setiv(cipher, iv, iv_len) @@ -274,7 +281,7 @@ int privatekey_dek_header(char *header, unsigned int header_len, int *algo, int return 1; } -BUFFER *privatekey_file_to_buffer(FILE *fp, int type, int cb(char *, int , int , char *), char *desc) +BUFFER *privatekey_file_to_buffer(FILE *fp, int type, ssh_auth_callback cb, void *userdata, char *desc) { char buf[MAXLINESIZE]; char *header_begin; @@ -353,7 +360,7 @@ BUFFER *privatekey_file_to_buffer(FILE *fp, int type, int cb(char *, int , int , buffer_free(buffer); if (algo) { - if (!privatekey_decrypt(algo, mode, key_len, iv, iv_len, ret, cb, desc)) + if (!privatekey_decrypt(algo, mode, key_len, iv, iv_len, ret, cb, userdata, desc)) { free(iv); return NULL; @@ -364,7 +371,7 @@ BUFFER *privatekey_file_to_buffer(FILE *fp, int type, int cb(char *, int , int , } int read_rsa_privatekey(FILE *fp, gcry_sexp_t *r, - int cb(char *, int , int , char *), char *desc) + ssh_auth_callback cb, void *userdata, char *desc) { STRING *n; STRING *e; @@ -377,7 +384,7 @@ int read_rsa_privatekey(FILE *fp, gcry_sexp_t *r, STRING *v; BUFFER *buffer; - if (!(buffer=privatekey_file_to_buffer(fp, TYPE_RSA, cb, desc))) + if (!(buffer=privatekey_file_to_buffer(fp, TYPE_RSA, cb, userdata, desc))) return 0; if (!asn1_check_sequence(buffer)) { @@ -414,7 +421,7 @@ int read_rsa_privatekey(FILE *fp, gcry_sexp_t *r, return 1; } -int read_dsa_privatekey(FILE *fp, gcry_sexp_t *r, int cb(char *, int , int , char *), char *desc) +int read_dsa_privatekey(FILE *fp, gcry_sexp_t *r, ssh_auth_callback cb, void *userdata; char *desc) { STRING *p; STRING *q; @@ -424,7 +431,7 @@ int read_dsa_privatekey(FILE *fp, gcry_sexp_t *r, int cb(char *, int , int , cha STRING *v; BUFFER *buffer; - if (!(buffer=privatekey_file_to_buffer(fp, TYPE_DSS, cb, desc))) + if (!(buffer=privatekey_file_to_buffer(fp, TYPE_DSS, cb, userdata, desc))) return 0; if (!asn1_check_sequence(buffer)) { @@ -456,18 +463,23 @@ int read_dsa_privatekey(FILE *fp, gcry_sexp_t *r, int cb(char *, int , int , cha } #endif /* GCRYPT */ +static int pem_get_password(char *buf, int size, int rwflag, void *userdata) { + SSH_SESSION *session = userdata; -/* completely deprecated */ -static int default_get_password(char *buf, int size,int rwflag, char *descr){ - memset(buf,0,size); - return 0; -} + ZERO_STRUCTP(buf); + + if (session && session->options->auth_function) { + if ((*session->options->auth_function)("Passphrase for private key:", buf, size, 0, 0, + session->options->auth_userdata ? session->options->auth_userdata : NULL) < 0) { + return 0; + } -/* in case the passphrase has been given in parameter */ -static int get_password_specified(char *buf,int size, int rwflag, char *password){ - snprintf(buf,size,"%s",password); return strlen(buf); + } + + return 0; } + /** \addtogroup ssh_auth * @{ */ @@ -481,9 +493,11 @@ static int get_password_specified(char *buf,int size, int rwflag, char *password * \see private_key_free() * \see publickey_from_privatekey() */ -PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type,char *passphrase){ +PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type, const char *passphrase){ FILE *file=fopen(filename,"r"); PRIVATE_KEY *privkey; + ssh_auth_callback auth_cb = NULL; + void *auth_ud = NULL; #ifdef HAVE_LIBGCRYPT gcry_sexp_t dsa=NULL; gcry_sexp_t rsa=NULL; @@ -498,24 +512,28 @@ PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type, } if(type==TYPE_DSS){ if(!passphrase){ - if(session && session->options->passphrase_function) + if (session && session->options->auth_function) { + auth_cb = session->options->auth_function; + if (session->options->auth_userdata) { + auth_ud = session->options->auth_userdata; + } #ifdef HAVE_LIBGCRYPT - valid = read_dsa_privatekey(file,&dsa, session->options->passphrase_function,"DSA private key"); - else - valid = read_dsa_privatekey(file,&dsa,(void *)default_get_password, "DSA private key"); + valid = read_dsa_privatekey(file,&dsa, auth_cb, auth_ud, "Passphrase for private key:"); + } + } else { + valid = read_dsa_privatekey(file,&dsa, NULL, passphrase, NULL); } - else - valid = read_dsa_privatekey(file,&dsa,(void *)get_password_specified,passphrase); fclose(file); - if(!valid){ - ssh_set_error(session,SSH_FATAL,"parsing private key %s",filename); + if(!valid) { + ssh_set_error(session,SSH_FATAL,"parsing private key %s",filename); #elif defined HAVE_LIBCRYPTO - dsa=PEM_read_DSAPrivateKey(file,NULL, session->options->passphrase_function,"DSA private key"); - else - dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)default_get_password, "DSA private key"); + dsa = PEM_read_DSAPrivateKey(file,NULL, pem_get_password, session); + } else { + dsa = PEM_read_DSAPrivateKey(file,NULL, NULL, NULL); + } + } else { + dsa = PEM_read_DSAPrivateKey(file, NULL, NULL, (void *) passphrase); } - else - dsa=PEM_read_DSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase); fclose(file); if(!dsa){ ssh_set_error(session,SSH_FATAL,"parsing private key %s" @@ -526,24 +544,28 @@ PRIVATE_KEY *privatekey_from_file(SSH_SESSION *session,char *filename,int type, } else if (type==TYPE_RSA){ if(!passphrase){ - if(session && session->options->passphrase_function) + if(session && session->options->auth_function) { + auth_cb = session->options->auth_function; + if (session->options->auth_userdata) { + auth_ud = session->options->auth_userdata; + } #ifdef HAVE_LIBGCRYPT - valid = read_rsa_privatekey(file,&rsa, session->options->passphrase_function,"RSA private key"); - else - valid = read_rsa_privatekey(file,&rsa,(void *)default_get_password, "RSA private key"); + valid = read_rsa_privatekey(file, &rsa, auth_cb, auth_ud, "Passphrase for private key:"); + } + } else { + valid = read_rsa_privatekey(file, &rsa, NULL, passphrase, NULL); } - else - valid = read_rsa_privatekey(file,&rsa,(void *)get_password_specified,passphrase); fclose(file); if(!valid){ ssh_set_error(session,SSH_FATAL,"parsing private key %s",filename); #elif defined HAVE_LIBCRYPTO - rsa=PEM_read_RSAPrivateKey(file,NULL, session->options->passphrase_function,"RSA private key"); - else - rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)default_get_password, "RSA private key"); + rsa = PEM_read_RSAPrivateKey(file, NULL, pem_get_password, session); + } else { + rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL); + } + } else { + rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, (void *) passphrase); } - else - rsa=PEM_read_RSAPrivateKey(file,NULL,(void *)get_password_specified,passphrase); fclose(file); if(!rsa){ ssh_set_error(session,SSH_FATAL,"parsing private key %s" diff --git a/libssh/options.c b/libssh/options.c index 762980d2..9de84ad4 100644 --- a/libssh/options.c +++ b/libssh/options.c @@ -101,7 +101,8 @@ SSH_OPTIONS *ssh_options_copy(SSH_OPTIONS *opt){ for(i=0;i<10;++i) if(opt->wanted_methods[i]) ret->wanted_methods[i]=strdup(opt->wanted_methods[i]); - ret->passphrase_function=opt->passphrase_function; + ret->auth_function=opt->auth_function; + ret->auth_userdata=opt->auth_userdata; ret->connect_status_function=opt->connect_status_function; ret->connect_status_arg=opt->connect_status_arg; ret->timeout=opt->timeout; @@ -571,6 +572,14 @@ int ssh_options_getopt(SSH_OPTIONS *options, int *argcptr, char **argv){ return 0 ; } +void ssh_options_set_auth_callback(SSH_OPTIONS *opt, ssh_auth_callback cb, + void *userdata) { + if (opt == NULL) { + return; + } + opt->auth_function = cb; + opt->auth_userdata = userdata; +} /** @} */ diff --git a/sample.c b/sample.c index 27111a82..94b67172 100644 --- a/sample.c +++ b/sample.c @@ -454,7 +454,7 @@ int main(int argc, char **argv){ printf("\n"); /* no ? you should :) */ - auth=ssh_userauth_autopubkey(session); + auth=ssh_userauth_autopubkey(session, NULL); if(auth==SSH_AUTH_ERROR){ fprintf(stderr,"Authenticating with pubkey: %s\n",ssh_get_error(session)); ssh_finalize();