/* * prompt.c -- ask the user for authentication information. * * ==================================================================== * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */ /* ==================================================================== */ /*** Includes. ***/ #include #include #include "svn_cmdline.h" #include "svn_string.h" #include "svn_auth.h" #include "svn_error.h" #include "svn_private_config.h" /* Wait for input on @a *f. Doing all allocations * in @a pool. This functions is based on apr_wait_for_io_or_timeout(). * Note that this will return an EINTR on a signal. * * ### FIX: When APR gives us a better way of doing this use it. */ static apr_status_t wait_for_input(apr_file_t *f, apr_pool_t *pool) { apr_pollfd_t pollset; int srv, n; /* APR specs say things that are unimplemented are supposed to return * APR_ENOTIMPL. But when trying to use APR_POLL_FILE with apr_poll * on Windows it returns APR_EBADF instead. So just return APR_ENOTIMPL * ourselves here. */ #ifdef WIN32 return APR_ENOTIMPL; #endif /* WIN32 */ pollset.desc_type = APR_POLL_FILE; pollset.desc.f = f; pollset.p = pool; pollset.reqevents = APR_POLLIN; #ifndef AS400 srv = apr_poll(&pollset, 1, &n, -1); #else /* OS400 requires a pool argument for apr_poll(). */ srv = apr_poll(&pollset, 1, &n, -1, pool); #endif if (n == 1 && pollset.rtnevents & APR_POLLIN) return APR_SUCCESS; return srv; } /* Set @a *result to the result of prompting the user with @a * prompt_msg. Use @ *pb to get the cancel_func and cancel_baton. * Do not call the cancel_func if @a *pb is NULL. * Allocate @a *result in @a pool. * * If @a hide is true, then try to avoid displaying the user's input. */ static svn_error_t * prompt(const char **result, const char *prompt_msg, svn_boolean_t hide, svn_cmdline_prompt_baton_t *pb, apr_pool_t *pool) { apr_status_t status; apr_file_t *fp; char c; svn_stringbuf_t *strbuf = svn_stringbuf_create("", pool); status = apr_file_open_stdin(&fp, pool); if (status) return svn_error_wrap_apr(status, _("Can't open stdin")); if (! hide) { svn_boolean_t saw_first_half_of_eol = FALSE; SVN_ERR(svn_cmdline_fputs(prompt_msg, stderr, pool)); fflush(stderr); while (1) { /* Hack to allow us to not block for io on the prompt, so * we can cancel. */ if (pb) SVN_ERR(pb->cancel_func(pb->cancel_baton)); status = wait_for_input(fp, pool); if (APR_STATUS_IS_EINTR(status)) continue; else if (status && status != APR_ENOTIMPL) return svn_error_wrap_apr(status, _("Can't read stdin")); status = apr_file_getc(&c, fp); if (status) return svn_error_wrap_apr(status, _("Can't read stdin")); if (saw_first_half_of_eol) { if (c == APR_EOL_STR[1]) break; else saw_first_half_of_eol = FALSE; } else if (c == APR_EOL_STR[0]) { if (sizeof(APR_EOL_STR) == 3) { saw_first_half_of_eol = TRUE; continue; } else if (sizeof(APR_EOL_STR) == 2) break; else /* ### APR_EOL_STR holds more than two chars? Who ever heard of such a thing? */ abort(); } svn_stringbuf_appendbytes(strbuf, &c, 1); } } else { const char *prompt_stdout; size_t bufsize = 300; SVN_ERR(svn_cmdline_cstring_from_utf8(&prompt_stdout, prompt_msg, pool)); svn_stringbuf_ensure(strbuf, bufsize); status = apr_password_get(prompt_stdout, strbuf->data, &bufsize); if (status) return svn_error_wrap_apr(status, _("Can't get password")); } SVN_ERR(svn_cmdline_cstring_to_utf8(result, strbuf->data, pool)); return SVN_NO_ERROR; } /** Prompt functions for auth providers. **/ /* Helper function for auth provider prompters: mention the * authentication @a realm on stderr, in a manner appropriate for * preceding a prompt; or if @a realm is null, then do nothing. */ static svn_error_t * maybe_print_realm(const char *realm, apr_pool_t *pool) { if (realm) { SVN_ERR(svn_cmdline_fprintf(stderr, pool, _("Authentication realm: %s\n"), realm)); fflush(stderr); } return SVN_NO_ERROR; } /* This implements 'svn_auth_simple_prompt_func_t'. */ svn_error_t * svn_cmdline_auth_simple_prompt(svn_auth_cred_simple_t **cred_p, void *baton, const char *realm, const char *username, svn_boolean_t may_save, apr_pool_t *pool) { svn_auth_cred_simple_t *ret = apr_pcalloc(pool, sizeof(*ret)); const char *pass_prompt; svn_cmdline_prompt_baton_t *pb = baton; SVN_ERR(maybe_print_realm(realm, pool)); if (username) ret->username = apr_pstrdup(pool, username); else SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); pass_prompt = apr_psprintf(pool, _("Password for '%s': "), ret->username); SVN_ERR(prompt(&(ret->password), pass_prompt, TRUE, pb, pool)); ret->may_save = may_save; *cred_p = ret; return SVN_NO_ERROR; } /* This implements 'svn_auth_username_prompt_func_t'. */ svn_error_t * svn_cmdline_auth_username_prompt(svn_auth_cred_username_t **cred_p, void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool) { svn_auth_cred_username_t *ret = apr_pcalloc(pool, sizeof(*ret)); svn_cmdline_prompt_baton_t *pb = baton; SVN_ERR(maybe_print_realm(realm, pool)); SVN_ERR(prompt(&(ret->username), _("Username: "), FALSE, pb, pool)); ret->may_save = may_save; *cred_p = ret; return SVN_NO_ERROR; } /* This implements 'svn_auth_ssl_server_trust_prompt_func_t'. */ svn_error_t * svn_cmdline_auth_ssl_server_trust_prompt (svn_auth_cred_ssl_server_trust_t **cred_p, void *baton, const char *realm, apr_uint32_t failures, const svn_auth_ssl_server_cert_info_t *cert_info, svn_boolean_t may_save, apr_pool_t *pool) { const char *choice; svn_stringbuf_t *msg; svn_cmdline_prompt_baton_t *pb = baton; svn_stringbuf_t *buf = svn_stringbuf_createf (pool, _("Error validating server certificate for '%s':\n"), realm); if (failures & SVN_AUTH_SSL_UNKNOWNCA) { svn_stringbuf_appendcstr (buf, _(" - The certificate is not issued by a trusted authority. Use the\n" " fingerprint to validate the certificate manually!\n")); } if (failures & SVN_AUTH_SSL_CNMISMATCH) { svn_stringbuf_appendcstr (buf, _(" - The certificate hostname does not match.\n")); } if (failures & SVN_AUTH_SSL_NOTYETVALID) { svn_stringbuf_appendcstr (buf, _(" - The certificate is not yet valid.\n")); } if (failures & SVN_AUTH_SSL_EXPIRED) { svn_stringbuf_appendcstr (buf, _(" - The certificate has expired.\n")); } if (failures & SVN_AUTH_SSL_OTHER) { svn_stringbuf_appendcstr (buf, _(" - The certificate has an unknown error.\n")); } msg = svn_stringbuf_createf (pool, _("Certificate information:\n" " - Hostname: %s\n" " - Valid: from %s until %s\n" " - Issuer: %s\n" " - Fingerprint: %s\n"), cert_info->hostname, cert_info->valid_from, cert_info->valid_until, cert_info->issuer_dname, cert_info->fingerprint); svn_stringbuf_appendstr(buf, msg); if (may_save) { svn_stringbuf_appendcstr (buf, _("(R)eject, accept (t)emporarily or accept (p)ermanently? ")); } else { svn_stringbuf_appendcstr(buf, _("(R)eject or accept (t)emporarily? ")); } SVN_ERR(prompt(&choice, buf->data, FALSE, pb, pool)); if (choice && (choice[0] == 't' || choice[0] == 'T')) { *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); (*cred_p)->may_save = FALSE; (*cred_p)->accepted_failures = failures; } else if (may_save && choice && (choice[0] == 'p' || choice[0] == 'P')) { *cred_p = apr_pcalloc(pool, sizeof(**cred_p)); (*cred_p)->may_save = TRUE; (*cred_p)->accepted_failures = failures; } else { *cred_p = NULL; } return SVN_NO_ERROR; } /* This implements 'svn_auth_ssl_client_cert_prompt_func_t'. */ svn_error_t * svn_cmdline_auth_ssl_client_cert_prompt (svn_auth_cred_ssl_client_cert_t **cred_p, void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool) { svn_auth_cred_ssl_client_cert_t *cred = NULL; const char *cert_file = NULL; svn_cmdline_prompt_baton_t *pb = baton; SVN_ERR(maybe_print_realm(realm, pool)); SVN_ERR(prompt(&cert_file, _("Client certificate filename: "), FALSE, pb, pool)); cred = apr_palloc(pool, sizeof(*cred)); cred->cert_file = cert_file; cred->may_save = may_save; *cred_p = cred; return SVN_NO_ERROR; } /* This implements 'svn_auth_ssl_client_cert_pw_prompt_func_t'. */ svn_error_t * svn_cmdline_auth_ssl_client_cert_pw_prompt (svn_auth_cred_ssl_client_cert_pw_t **cred_p, void *baton, const char *realm, svn_boolean_t may_save, apr_pool_t *pool) { svn_auth_cred_ssl_client_cert_pw_t *cred = NULL; const char *result; const char *text = apr_psprintf(pool, _("Passphrase for '%s': "), realm); svn_cmdline_prompt_baton_t *pb = baton; SVN_ERR(prompt(&result, text, TRUE, pb, pool)); cred = apr_pcalloc(pool, sizeof(*cred)); cred->password = result; cred->may_save = may_save; *cred_p = cred; return SVN_NO_ERROR; } /** Generic prompting. **/ svn_error_t * svn_cmdline_prompt_user(const char **result, const char *prompt_str, apr_pool_t *pool) { return prompt(result, prompt_str, FALSE /* don't hide input */, NULL, pool); }