1 #include "first.h" 2 3 /* mod_authn_pam 4 * 5 * FUTURE POTENTIAL PERFORMANCE ENHANCEMENTS: 6 * - database response is not cached 7 * TODO: db response caching (for limited time) to reduce load on db 8 * (only cache successful logins to prevent cache bloat?) 9 * (or limit number of entries (size) of cache) 10 * (maybe have negative cache (limited size) of names not found in database) 11 * - database query is synchronous and blocks waiting for response 12 */ 13 14 #include <security/pam_appl.h> 15 16 #include "base.h" 17 #include "http_auth.h" 18 #include "log.h" 19 #include "plugin.h" 20 21 #include <stdlib.h> 22 #include <string.h> 23 24 typedef struct { 25 array *opts; 26 const char *service; 27 } plugin_config; 28 29 typedef struct { 30 PLUGIN_DATA; 31 plugin_config **config_storage; 32 plugin_config conf; 33 } plugin_data; 34 35 static handler_t mod_authn_pam_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); 36 37 INIT_FUNC(mod_authn_pam_init) { 38 static http_auth_backend_t http_auth_backend_pam = 39 { "pam", mod_authn_pam_basic, NULL, NULL }; 40 plugin_data *p = calloc(1, sizeof(*p)); 41 42 /* register http_auth_backend_pam */ 43 http_auth_backend_pam.p_d = p; 44 http_auth_backend_set(&http_auth_backend_pam); 45 46 return p; 47 } 48 49 FREE_FUNC(mod_authn_pam_free) { 50 plugin_data *p = p_d; 51 if (!p) return HANDLER_GO_ON; 52 53 if (p->config_storage) { 54 for (size_t i = 0; i < srv->config_context->used; ++i) { 55 plugin_config *s = p->config_storage[i]; 56 if (NULL == s) continue; 57 array_free(s->opts); 58 free(s); 59 } 60 free(p->config_storage); 61 } 62 free(p); 63 UNUSED(srv); 64 return HANDLER_GO_ON; 65 } 66 67 SETDEFAULTS_FUNC(mod_authn_pam_set_defaults) { 68 plugin_data *p = p_d; 69 config_values_t cv[] = { 70 { "auth.backend.pam.opts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, 71 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 72 }; 73 74 p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); 75 76 for (size_t i = 0; i < srv->config_context->used; ++i) { 77 data_config const *config = (data_config const*)srv->config_context->data[i]; 78 data_string *ds; 79 plugin_config *s = calloc(1, sizeof(plugin_config)); 80 s->opts = array_init(); 81 82 cv[0].destination = s->opts; 83 84 p->config_storage[i] = s; 85 86 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 87 return HANDLER_ERROR; 88 } 89 90 if (0 == s->opts->used) continue; 91 92 ds = (data_string *) 93 array_get_element_klen(s->opts, CONST_STR_LEN("service")); 94 s->service = (NULL != ds) ? ds->value->ptr : "http"; 95 } 96 97 if (p->config_storage[0]->service == NULL) 98 p->config_storage[0]->service = "http"; 99 100 return HANDLER_GO_ON; 101 } 102 103 #define PATCH(x) \ 104 p->conf.x = s->x; 105 static int mod_authn_pam_patch_connection(server *srv, connection *con, plugin_data *p) { 106 plugin_config *s = p->config_storage[0]; 107 PATCH(service); 108 109 /* skip the first, the global context */ 110 for (size_t i = 1; i < srv->config_context->used; ++i) { 111 data_config *dc = (data_config *)srv->config_context->data[i]; 112 113 /* condition didn't match */ 114 if (!config_check_cond(srv, con, dc)) continue; 115 116 /* merge config */ 117 s = p->config_storage[i]; 118 for (size_t j = 0; j < dc->value->used; ++j) { 119 data_unset *du = dc->value->data[j]; 120 if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.pam.opts"))) { 121 PATCH(service); 122 } 123 } 124 } 125 126 return 0; 127 } 128 #undef PATCH 129 130 static int mod_authn_pam_fn_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { 131 const char * const pw = (char *)appdata_ptr; 132 struct pam_response * const pr = *resp = 133 (struct pam_response *)malloc(num_msg * sizeof(struct pam_response)); 134 for (int i = 0; i < num_msg; ++i) { 135 const int style = msg[i]->msg_style; 136 pr[i].resp_retcode = 0; 137 pr[i].resp = (style==PAM_PROMPT_ECHO_OFF || style==PAM_PROMPT_ECHO_ON) 138 ? strdup(pw) 139 : NULL; 140 } 141 return PAM_SUCCESS; 142 } 143 144 static handler_t mod_authn_pam_query(server *srv, connection *con, void *p_d, const buffer *username, const char *realm, const char *pw) { 145 plugin_data *p = (plugin_data *)p_d; 146 pam_handle_t *pamh = NULL; 147 struct pam_conv conv = { mod_authn_pam_fn_conv, NULL }; 148 const int flags = PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK; 149 int rc; 150 UNUSED(realm); 151 *(const char **)&conv.appdata_ptr = pw; /*(cast away const)*/ 152 153 mod_authn_pam_patch_connection(srv, con, p); 154 155 rc = pam_start(p->conf.service, username->ptr, &conv, &pamh); 156 if (PAM_SUCCESS != rc 157 || PAM_SUCCESS !=(rc = pam_set_item(pamh,PAM_RHOST,con->dst_addr_buf->ptr)) 158 || PAM_SUCCESS !=(rc = pam_authenticate(pamh, flags)) 159 || PAM_SUCCESS !=(rc = pam_acct_mgmt(pamh, flags))) 160 log_error_write(srv, __FILE__, __LINE__, "ss", 161 "pam:", pam_strerror(pamh, rc)); 162 pam_end(pamh, rc); 163 return (PAM_SUCCESS == rc) ? HANDLER_GO_ON : HANDLER_ERROR; 164 } 165 166 static handler_t mod_authn_pam_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { 167 char *realm = require->realm->ptr; 168 handler_t rc = mod_authn_pam_query(srv, con, p_d, username, realm, pw); 169 if (HANDLER_GO_ON != rc) return rc; 170 return http_auth_match_rules(require, username->ptr, NULL, NULL) 171 ? HANDLER_GO_ON /* access granted */ 172 : HANDLER_ERROR; 173 } 174 175 int mod_authn_pam_plugin_init(plugin *p); 176 int mod_authn_pam_plugin_init(plugin *p) { 177 p->version = LIGHTTPD_VERSION_ID; 178 p->name = buffer_init_string("authn_pam"); 179 p->data = NULL; 180 p->init = mod_authn_pam_init; 181 p->cleanup = mod_authn_pam_free; 182 p->set_defaults= mod_authn_pam_set_defaults; 183 184 return 0; 185 } 186