diff options
Diffstat (limited to 'src/auth.cpp')
-rw-r--r-- | src/auth.cpp | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/src/auth.cpp b/src/auth.cpp new file mode 100644 index 0000000..ff5c4bc --- /dev/null +++ b/src/auth.cpp @@ -0,0 +1,231 @@ +/* + Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <cassert> +#include "auth.h" +#include "log.h" +#include "protocol.h" +#include "user.h" +#include "userintf.h" +#include "util.h" + +extern string user_host; + +t_cr_cache_entry:: t_cr_cache_entry(const t_url &_to, const t_credentials &_cr, + const string &_passwd, bool _proxy) : + to(_to) +{ + credentials = _cr; + passwd = _passwd; + proxy = _proxy; +} + + +list<t_cr_cache_entry>::iterator t_auth::find_cache_entry( + const t_url &_to, const string &realm, bool proxy) +{ + for (list<t_cr_cache_entry>::iterator i = cache.begin(); + i != cache.end(); i++) + { + // RFC 3261 22.1 + // Only the realm determines the protection space. + // So the to-uri must not need to be compared as for HTTP + // i.e. check i->to == _to must not be done. + // As realm strings are globally unique there is no need + // to check if the credentials are for a 407 or 401 response. + // i.e. check i->proxy == proxy is not needed. + if (i->credentials.digest_response.realm == realm) + { + return i; + } + } + + return cache.end(); +} + +void t_auth::update_cache(const t_url &to, const t_credentials &cr, + const string &passwd, bool proxy) +{ + list<t_cr_cache_entry>::iterator i, j; + + i = find_cache_entry(to, cr.digest_response.realm, proxy); + + if (i == cache.end()) { + if (cache.size() > AUTH_CACHE_SIZE) { + cache.erase(cache.begin()); + } + cache.push_back(t_cr_cache_entry(to, cr, passwd, proxy)); + } else { + i->credentials = cr; + i->passwd = passwd; + + // Move cache entry to end of the cache. + // TODO: this can be more efficient by checking if the + // entry is already at the end. + t_cr_cache_entry e = *i; + cache.erase(i); + cache.push_back(e); + } +} + +bool t_auth::auth_failed(t_request *r, const t_challenge &c, + bool proxy) const +{ + if (c.digest_challenge.stale) { + log_file->write_report("Stale nonce value.", "t_auth::auth_failed"); + return false; + } + + if (proxy) { + return r->hdr_proxy_authorization.contains( + c.digest_challenge.realm, r->uri); + } else { + return r->hdr_authorization.contains( + c.digest_challenge.realm, r->uri); + } +} + +void t_auth::remove_credentials(t_request *r, const t_challenge &c, + bool proxy) const +{ + if (proxy) { + r->hdr_proxy_authorization.remove_credentials( + c.digest_challenge.realm, r->uri); + } else { + r->hdr_authorization.remove_credentials( + c.digest_challenge.realm, r->uri); + } +} + +t_auth::t_auth() { + re_register = false; +} + +bool t_auth::authorize(t_user *user_config, t_request *r, t_response *resp) { + string username; + string passwd; + list<t_cr_cache_entry>::iterator i; + t_challenge c; + bool proxy; + + assert(resp->must_authenticate()); + + if (resp->code == R_401_UNAUTHORIZED) { + c = resp->hdr_www_authenticate.challenge; + proxy = false; + } else { + c = resp->hdr_proxy_authenticate.challenge; + proxy = true; + } + + // Only DIGEST is supported + if (c.auth_scheme != AUTH_DIGEST) { + log_file->write_header("t_auth::authorize"); + log_file->write_raw("Unsupported authentication scheme: "); + log_file->write_raw(c.auth_scheme); + log_file->write_endl(); + log_file->write_footer(); + return false; + } + + const t_digest_challenge &dc = c.digest_challenge; + i = find_cache_entry(r->uri, dc.realm, proxy); + + if (auth_failed(r, c, proxy)) { + // The current credentials are wrong. Remove them and + // ask the user for a username and password. + remove_credentials(r, c, proxy); + } else { + // Determine user name and password + if (i != cache.end()) { + username = i->credentials.digest_response.username; + passwd = i->passwd; + } else if (dc.realm == user_config->get_auth_realm() || + user_config->get_auth_realm() == "") { + username = user_config->get_auth_name(); + passwd = user_config->get_auth_pass(); + } + + if (dc.stale) { + // The current credentials are stale. Remove them. + remove_credentials(r, c, proxy); + } + } + + // Ask user for username/password + if ((username == "" || passwd == "") && !re_register) { + if (!ui->cb_ask_credentials(user_config, dc.realm, username, passwd)) { + log_file->write_report("Asking user name and password failed.", + "t_auth::authorize"); + return false; + } + } + + // No valid username/passwd + if (username == "" && passwd == "") { + log_file->write_report("Incorrect user name and/or password.", + "t_auth::authorize"); + return false; + } + + bool auth_success; + string fail_reason; + if (!proxy) { + t_credentials cr; + auth_success = r->www_authorize(c, user_config, + username, passwd, 1, NEW_CNONCE, cr, fail_reason); + + if (auth_success) { + update_cache(r->uri, cr, passwd, proxy); + } + } else { + t_credentials cr; + auth_success = r->proxy_authorize(c, user_config, + username, passwd, 1, NEW_CNONCE, cr, fail_reason); + + if (auth_success) { + update_cache(r->uri, cr, passwd, proxy); + } + } + + if (!auth_success) { + log_file->write_report(fail_reason, "t_auth::authorize"); + return false; + } + + return true; +} + +void t_auth::remove_from_cache(const string &realm) { + if (realm.empty()) { + cache.clear(); + } else { + list<t_cr_cache_entry>::iterator i = find_cache_entry(t_url(), realm); + if (i != cache.end()) { + cache.erase(i); + } + } +} + +void t_auth::set_re_register(bool on) { + re_register = on; +} + +bool t_auth::get_re_register(void) const { + return re_register; +} |