/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* Cherokee * * Authors: * Alvaro Lopez Ortega * * Copyright (C) 2001-2007 Alvaro Lopez Ortega * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * 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 "common-internal.h" #include "validator.h" #include "connection.h" #include "connection-protected.h" #include "header-protected.h" #include "config_entry.h" #include "util.h" #define ENTRIES "validator" ret_t cherokee_validator_init_base (cherokee_validator_t *validator, cherokee_validator_props_t *props, cherokee_plugin_info_validator_t *info) { cherokee_module_init_base (MODULE(validator), MODULE_PROPS(props), PLUGIN_INFO(info)); validator->check = NULL; cherokee_buffer_init (&validator->user); cherokee_buffer_init (&validator->passwd); cherokee_buffer_init (&validator->realm); cherokee_buffer_init (&validator->response); cherokee_buffer_init (&validator->uri); cherokee_buffer_init (&validator->qop); cherokee_buffer_init (&validator->nonce); cherokee_buffer_init (&validator->cnonce); cherokee_buffer_init (&validator->algorithm); cherokee_buffer_init (&validator->nc); return ret_ok; } ret_t cherokee_validator_free_base (cherokee_validator_t *validator) { cherokee_buffer_mrproper (&validator->user); cherokee_buffer_mrproper (&validator->passwd); cherokee_buffer_mrproper (&validator->realm); cherokee_buffer_mrproper (&validator->response); cherokee_buffer_mrproper (&validator->uri); cherokee_buffer_mrproper (&validator->qop); cherokee_buffer_mrproper (&validator->nonce); cherokee_buffer_mrproper (&validator->cnonce); cherokee_buffer_mrproper (&validator->algorithm); cherokee_buffer_mrproper (&validator->nc); free (validator); return ret_ok; } ret_t cherokee_validator_free (cherokee_validator_t *validator) { module_func_free_t func; return_if_fail (validator!=NULL, ret_error); func = (module_func_free_t) MODULE(validator)->free; if (func == NULL) { return ret_error; } return func (validator); } ret_t cherokee_validator_check (cherokee_validator_t *validator, void *conn) { if (validator->check == NULL) { return ret_error; } return validator->check (validator, CONN(conn)); } ret_t cherokee_validator_add_headers (cherokee_validator_t *validator, void *conn, cherokee_buffer_t *buf) { if (validator->add_headers == NULL) { return ret_error; } return validator->add_headers (validator, CONN(conn), buf); } ret_t cherokee_validator_parse_basic (cherokee_validator_t *validator, char *str, cuint_t str_len) { char *colon; cherokee_buffer_t auth = CHEROKEE_BUF_INIT; /* Decode base64 */ cherokee_buffer_add (&auth, str, str_len); cherokee_buffer_decode_base64 (&auth); /* Look for the user:passwd structure */ colon = strchr (auth.buf, ':'); if (colon == NULL) goto error; /* Copy user and password */ cherokee_buffer_add (&validator->user, auth.buf, colon - auth.buf); cherokee_buffer_add (&validator->passwd, colon+1, auth.len - ((colon+1) - auth.buf)); TRACE (ENTRIES, "Parse basic auth got user=%s, passwd=%s\n", validator->user.buf, validator->passwd.buf); /* Clean up and exit */ cherokee_buffer_mrproper (&auth); return ret_ok; error: cherokee_buffer_mrproper (&auth); return ret_error; } ret_t cherokee_validator_parse_digest (cherokee_validator_t *validator, char *str, cuint_t str_len) { cuint_t len; char *end; char *entry; char *comma; char *equal; cherokee_buffer_t auth = CHEROKEE_BUF_INIT; cherokee_buffer_t *entry_buf; /* Copy authentication string */ cherokee_buffer_add (&auth, str, str_len); entry = auth.buf; end = auth.buf + auth.len; do { /* Skip some chars */ while ((*entry == CHR_SP) || (*entry == CHR_CR) || (*entry == CHR_LF)) entry++; /* Check for the end */ if (entry >= end) break; comma = strchr(entry, ','); if (equal_str (entry, "nc")) { entry_buf = &validator->nc; } else if (equal_str (entry, "uri")) { entry_buf = &validator->uri; } else if (equal_str (entry, "qop")) { entry_buf = &validator->qop; } else if (equal_str (entry, "realm")) { entry_buf = &validator->realm; } else if (equal_str (entry, "nonce")) { entry_buf = &validator->nonce; } else if (equal_str (entry, "cnonce")) { entry_buf = &validator->cnonce; } else if (equal_str (entry, "username")) { entry_buf = &validator->user; } else if (equal_str (entry, "response")) { entry_buf = &validator->response; } else if (equal_str (entry, "algorithm")) { entry_buf = &validator->algorithm; } else { entry = comma + 1; continue; } /* Split the string [1] */ if (comma) *comma = '\0'; equal = strchr (entry, '='); if (equal == NULL) { if (comma) *comma = ','; /* [1] */ continue; } /* Check for "s and skip it */ equal += (equal[1] == '"') ? 2 : 1; len = strlen(equal); while ((len > 0) && ((equal[len-1] == '"') || (equal[len-1] == CHR_CR) || (equal[len-1] == CHR_LF))) len--; /* Copy the entry value */ cherokee_buffer_add (entry_buf, equal, len); /* Resore [1], and prepare next loop */ if (comma) *comma = ','; entry = comma + 1; } while (comma != NULL); #if 0 printf ("nc %s\n", validator->nc.buf); printf ("uri %s\n", validator->uri.buf); printf ("qop %s\n", validator->qop.buf); printf ("realm %s\n", validator->realm.buf); printf ("nonce %s\n", validator->nonce.buf); printf ("cnonce %s\n", validator->cnonce.buf); printf ("username %s\n", validator->user.buf); printf ("reponse %s\n", validator->response.buf); printf ("algorithm %s\n", validator->algorithm.buf); #endif /* Clean up and exit */ cherokee_buffer_mrproper (&auth); return ret_ok; } static ret_t digest_HA2 (cherokee_validator_t *validator, cherokee_buffer_t *buf, cherokee_connection_t *conn) { ret_t ret; const char *method; cuint_t method_len; /* Sanity check */ if (cherokee_buffer_is_empty (&validator->uri)) return ret_deny; ret = cherokee_http_method_to_string (conn->header.method, &method, &method_len); if (unlikely (ret != ret_ok)) return ret; cherokee_buffer_ensure_size (buf, method_len + 1 + validator->uri.len); cherokee_buffer_add (buf, method, method_len); cherokee_buffer_add_str (buf, ":"); cherokee_buffer_add_buffer (buf, &validator->uri); cherokee_buffer_encode_md5_digest (buf); return ret_ok; } ret_t cherokee_validator_digest_response (cherokee_validator_t *validator, char *A1, cherokee_buffer_t *buf, cherokee_connection_t *conn) { ret_t ret; cherokee_buffer_t a2 = CHEROKEE_BUF_INIT; /* A1 has to be in string of lenght 32: * MD5_digest(user":"realm":"passwd) */ /* Sanity checks */ if (A1 == NULL) return ret_deny; if (cherokee_buffer_is_empty (&validator->nonce)) return ret_deny; /* Build A2 */ ret = digest_HA2 (validator, &a2, conn); if (ret != ret_ok) goto error; /* Build the final string */ cherokee_buffer_ensure_size (buf, 32 + a2.len + validator->nonce.len + 4); cherokee_buffer_add (buf, A1, 32); cherokee_buffer_add_str (buf, ":"); cherokee_buffer_add_buffer (buf, &validator->nonce); cherokee_buffer_add_str (buf, ":"); if (!cherokee_buffer_is_empty (&validator->qop)) { if (!cherokee_buffer_is_empty (&validator->nc)) cherokee_buffer_add_buffer (buf, &validator->nc); cherokee_buffer_add_str (buf, ":"); if (!cherokee_buffer_is_empty (&validator->cnonce)) cherokee_buffer_add_buffer (buf, &validator->cnonce); cherokee_buffer_add_str (buf, ":"); cherokee_buffer_add_buffer (buf, &validator->qop); cherokee_buffer_add_str (buf, ":"); } cherokee_buffer_add_buffer (buf, &a2); cherokee_buffer_encode_md5_digest (buf); cherokee_buffer_mrproper (&a2); return ret_ok; error: cherokee_buffer_mrproper (&a2); return ret; } ret_t cherokee_validator_digest_check (cherokee_validator_t *validator, cherokee_buffer_t *passwd, cherokee_connection_t *conn) { int re; ret_t ret; cherokee_buffer_t a1 = CHEROKEE_BUF_INIT; cherokee_buffer_t buf = CHEROKEE_BUF_INIT; /* Sanity check */ if (cherokee_buffer_is_empty (&validator->user) || cherokee_buffer_is_empty (&validator->realm)) return ret_deny; /* Build A1 */ cherokee_buffer_ensure_size (&a1, validator->user.len + 1 + validator->realm.len + 1 + passwd->len); cherokee_buffer_add_buffer (&a1, &validator->user); cherokee_buffer_add_str (&a1, ":"); cherokee_buffer_add_buffer (&a1, &validator->realm); cherokee_buffer_add_str (&a1, ":"); cherokee_buffer_add_buffer (&a1, passwd); cherokee_buffer_encode_md5_digest (&a1); /* Build a possible response */ ret = cherokee_validator_digest_response (validator, a1.buf, &buf, conn); if (unlikely(ret != ret_ok)) goto go_out; /* Compare and return */ re = cherokee_buffer_cmp_buf (&conn->validator->response, &buf); go_out: cherokee_buffer_mrproper (&a1); cherokee_buffer_mrproper (&buf); return (re == 0) ? ret_ok : ret_deny; } static ret_t add_method (char *method, void *data) { cherokee_config_entry_t *entry = CONF_ENTRY(data); if (equal_str (method, "basic")) entry->authentication |= http_auth_basic; else if (equal_str (method, "digest")) entry->authentication |= http_auth_digest; else { PRINT_MSG ("ERROR: Unknown authentication method '%s'\n", method); return ret_error; } return ret_ok; } static ret_t add_user (char *val, void *data) { return cherokee_avl_add_ptr (AVL(data), val, NULL); } ret_t cherokee_validator_configure (cherokee_config_node_t *conf, void *config_entry) { ret_t ret; cherokee_list_t *i; cherokee_config_node_t *subconf; cherokee_config_entry_t *entry = CONF_ENTRY(config_entry); cherokee_config_node_foreach (i, conf) { subconf = CONFIG_NODE(i); if (equal_buf_str (&subconf->key, "realm")) { ret = cherokee_buffer_dup (&subconf->val, &entry->auth_realm); if (ret != ret_ok) return ret; } else if (equal_buf_str (&subconf->key, "methods")) { ret = cherokee_config_node_read_list (subconf, NULL, add_method, entry); if (ret != ret_ok) return ret; } else if (equal_buf_str (&subconf->key, "users")) { if (entry->users == NULL) { cherokee_avl_new (&entry->users); } ret = cherokee_config_node_read_list (subconf, NULL, add_user, entry->users); if (ret != ret_ok) return ret; } } return ret_ok; } /* Validator properties */ ret_t cherokee_validator_props_init_base (cherokee_validator_props_t *props, module_func_props_free_t free_func) { props->valid_methods = http_auth_nothing; return cherokee_module_props_init_base (MODULE_PROPS(props), free_func); } ret_t cherokee_validator_props_free_base (cherokee_validator_props_t *props) { return cherokee_module_props_free_base (MODULE_PROPS(props)); }