xref: /lighttpd1.4/src/mod_authn_pam.c (revision e11f72b1)
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     const char *service;
26 } plugin_config;
27 
28 typedef struct {
29     PLUGIN_DATA;
30     plugin_config defaults;
31     plugin_config conf;
32 } plugin_data;
33 
34 static handler_t mod_authn_pam_basic(request_st *r, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw);
35 
36 INIT_FUNC(mod_authn_pam_init) {
37     static http_auth_backend_t http_auth_backend_pam =
38       { "pam", mod_authn_pam_basic, NULL, NULL };
39     plugin_data *p = calloc(1, sizeof(*p));
40 
41     /* register http_auth_backend_pam */
42     http_auth_backend_pam.p_d = p;
43     http_auth_backend_set(&http_auth_backend_pam);
44 
45     return p;
46 }
47 
48 static void mod_authn_pam_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
49     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
50       case 0: /* auth.backend.pam.opts */
51         if (cpv->vtype == T_CONFIG_LOCAL)
52             pconf->service = cpv->v.v;
53         break;
54       default:/* should not happen */
55         return;
56     }
57 }
58 
59 static void mod_authn_pam_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
60     do {
61         mod_authn_pam_merge_config_cpv(pconf, cpv);
62     } while ((++cpv)->k_id != -1);
63 }
64 
65 static void mod_authn_pam_patch_config(request_st * const r, plugin_data * const p) {
66     p->conf = p->defaults; /* copy small struct instead of memcpy() */
67     /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
68     for (int i = 1, used = p->nconfig; i < used; ++i) {
69         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
70             mod_authn_pam_merge_config(&p->conf,
71                                         p->cvlist + p->cvlist[i].v.u2[0]);
72     }
73 }
74 
75 SETDEFAULTS_FUNC(mod_authn_pam_set_defaults) {
76     static const config_plugin_keys_t cpk[] = {
77       { CONST_STR_LEN("auth.backend.pam.opts"),
78         T_CONFIG_ARRAY_KVSTRING,
79         T_CONFIG_SCOPE_CONNECTION }
80      ,{ NULL, 0,
81         T_CONFIG_UNSET,
82         T_CONFIG_SCOPE_UNSET }
83     };
84 
85     plugin_data * const p = p_d;
86     if (!config_plugin_values_init(srv, p, cpk, "mod_authn_pam"))
87         return HANDLER_ERROR;
88 
89     /* process and validate config directives
90      * (init i to 0 if global context; to 1 to skip empty global context) */
91     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
92         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
93         for (; -1 != cpv->k_id; ++cpv) {
94             switch (cpv->k_id) {
95               case 0: /* auth.backend.pam.opts */
96                 if (cpv->v.a->used) {
97                     const data_string *ds = (const data_string *)
98                       array_get_element_klen(cpv->v.a,CONST_STR_LEN("service"));
99                     cpv->v.v = (NULL != ds) ? ds->value.ptr : "http";
100                     cpv->vtype = T_CONFIG_LOCAL;
101                 }
102                 break;
103               default:/* should not happen */
104                 break;
105             }
106         }
107     }
108 
109     p->defaults.service = "http";
110 
111     /* initialize p->defaults from global config context */
112     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
113         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
114         if (-1 != cpv->k_id)
115             mod_authn_pam_merge_config(&p->defaults, cpv);
116     }
117 
118     return HANDLER_GO_ON;
119 }
120 
121 static int mod_authn_pam_fn_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)  {
122     const char * const pw = (char *)appdata_ptr;
123     struct pam_response * const pr = *resp =
124       (struct pam_response *)malloc(num_msg * sizeof(struct pam_response));
125     for (int i = 0; i < num_msg; ++i) {
126         const int style = msg[i]->msg_style;
127         pr[i].resp_retcode = 0;
128         pr[i].resp = (style==PAM_PROMPT_ECHO_OFF || style==PAM_PROMPT_ECHO_ON)
129           ? strdup(pw)
130           : NULL;
131     }
132     return PAM_SUCCESS;
133 }
134 
135 static handler_t mod_authn_pam_query(request_st * const r, void *p_d, const buffer * const username, const char * const realm, const char * const pw) {
136     plugin_data *p = (plugin_data *)p_d;
137     pam_handle_t *pamh = NULL;
138     struct pam_conv conv = { mod_authn_pam_fn_conv, NULL };
139     const int flags = PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK;
140     int rc;
141     UNUSED(realm);
142     *(const char **)&conv.appdata_ptr = pw; /*(cast away const)*/
143 
144     mod_authn_pam_patch_config(r, p);
145 
146     const char * const addrstr = r->con->dst_addr_buf->ptr;
147     rc = pam_start(p->conf.service, username->ptr, &conv, &pamh);
148     if (PAM_SUCCESS != rc
149      || PAM_SUCCESS !=(rc = pam_set_item(pamh, PAM_RHOST, addrstr))
150      || PAM_SUCCESS !=(rc = pam_authenticate(pamh, flags))
151      || PAM_SUCCESS !=(rc = pam_acct_mgmt(pamh, flags)))
152         log_error(r->conf.errh, __FILE__, __LINE__,
153           "pam: %s", pam_strerror(pamh, rc));
154     pam_end(pamh, rc);
155     return (PAM_SUCCESS == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
156 }
157 
158 static handler_t mod_authn_pam_basic(request_st * const r, void *p_d, const http_auth_require_t * const require, const buffer * const username, const char * const pw) {
159     char *realm = require->realm->ptr;
160     handler_t rc = mod_authn_pam_query(r, p_d, username, realm, pw);
161     if (HANDLER_GO_ON != rc) return rc;
162     return http_auth_match_rules(require, username->ptr, NULL, NULL)
163       ? HANDLER_GO_ON  /* access granted */
164       : HANDLER_ERROR;
165 }
166 
167 int mod_authn_pam_plugin_init(plugin *p);
168 int mod_authn_pam_plugin_init(plugin *p) {
169     p->version     = LIGHTTPD_VERSION_ID;
170     p->name        = "authn_pam";
171     p->init        = mod_authn_pam_init;
172     p->set_defaults= mod_authn_pam_set_defaults;
173 
174     return 0;
175 }
176