]> mj.ucw.cz Git - subauth.git/blob - apache/mod_authn_subauth.c
More default socket path fixes
[subauth.git] / apache / mod_authn_subauth.c
1 /*
2  *      Apache Module for the Sub-authentication Daemon
3  *
4  *      (c) 2017 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <ucw/lib.h>
8 #include <ucw/mempool.h>
9 #include <ucw/string.h>
10 #include <ucw/trans.h>
11 #include <ucw-json/json.h>
12
13 #include <ap_config.h>
14 #include <httpd.h>
15 #include <http_config.h>
16 #include <http_core.h>
17 #include <http_log.h>
18 #include <http_request.h>
19 #include <mod_auth.h>
20
21 #include <stdlib.h>
22 #include <sys/socket.h>
23 #include <sys/types.h>
24 #include <sys/un.h>
25 #include <syslog.h>
26 #include <unistd.h>
27
28 #include "autoconf.h"
29
30 struct dir_config {
31         const char *socket;
32         const char *zone;
33 };
34
35 AP_MODULE_DECLARE_DATA extern module authn_subauth_module;
36
37 struct context {
38         request_rec *r;
39         struct dir_config *dir;
40         struct json_context *json;
41         struct json_node *reply;
42 };
43
44 static struct json_node *run_command(struct context *ctx, struct json_node *request)
45 {
46         ctx->reply = NULL;
47
48         int sk = socket(PF_UNIX, SOCK_SEQPACKET, 0);
49         if (sk < 0) {
50                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "SubAuth cannot create socket");
51                 return NULL;
52         }
53
54         const char *path = ctx->dir->socket ? : INSTALL_RUN_DIR "/subauthd/subauthd.socket";
55         struct sockaddr_un sun;
56         sun.sun_family = AF_UNIX;
57         if (strlen(path) >= sizeof(sun.sun_path)) {
58                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "SubAuth socket path too long");
59                 goto fail1;
60         }
61         strcpy(sun.sun_path, path);
62
63         if (connect(sk, (struct sockaddr *) &sun, sizeof(sun)) < 0) {
64                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "SubAuth cannot connect to server at %s", path);
65                 goto fail1;
66         }
67
68         struct fastbuf *rq_fb = fbgrow_create(4096);
69         json_write(ctx->json, rq_fb, request);
70         byte *rq_buf;
71         uint rq_len = fbgrow_get_buf(rq_fb, &rq_buf);
72         if (send(sk, rq_buf, rq_len, 0) < 0) {
73                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "SubAuth error sending to server");
74                 goto fail2;
75         }
76
77         uint rp_bufsize = 16384;
78         byte *rp_buf = xmalloc(rp_bufsize);
79         int rp_len = recv(sk, rp_buf, rp_bufsize, 0);
80         if (rp_len < 0) {
81                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "SubAuth error receiving from server");
82                 goto fail3;
83         }
84
85         struct fastbuf rp_fb;
86         fbbuf_init_read(&rp_fb, rp_buf, rp_len, 0);
87
88         struct json_node *reply = NULL;
89         TRANS_TRY {
90                 reply = json_parse(ctx->json, &rp_fb);
91         } TRANS_CATCH(x) {
92                 goto fail3;
93         } TRANS_END;
94
95         if (reply->type != JSON_OBJECT) {
96                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "SubAuth malformed server reply: Top-level node is not an object");
97                 goto fail3;
98         }
99
100         ctx->reply = reply;
101
102 fail3:
103         free(rp_buf);
104 fail2:
105         bclose(rq_fb);
106 fail1:
107         close(sk);
108         return ctx->reply;
109 }
110
111 static authn_status check_password(request_rec *r, const char *user, const char *password)
112 {
113         struct dir_config *dir = ap_get_module_config(r->per_dir_config, &authn_subauth_module);
114
115         if (!dir->zone) {
116                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "SubAuthZone not specified in the configuration");
117                 return AUTH_GENERAL_ERROR;
118         }
119
120         authn_status status = AUTH_GENERAL_ERROR;
121         struct context ctx0 = { .r = r, .dir = dir };
122         struct context *ctx = &ctx0;
123
124         ctx->json = json_new();
125         struct json_node *rq = json_new_object(ctx->json);
126         json_object_set(rq, "cmd", json_new_string(ctx->json, "login"));
127         json_object_set(rq, "zone", json_new_string(ctx->json, dir->zone));
128         json_object_set(rq, "login", json_new_string(ctx->json, user));
129         json_object_set(rq, "passwd", json_new_string(ctx->json, password));
130
131         struct json_node *rp = run_command(ctx, rq);
132         if (!rp)
133                 goto done;
134         struct json_node *error = json_object_get(rp, "error");
135         if (!error || error->type != JSON_STRING) {
136                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "SubAuth malformed server reply: No error status found");
137                 goto done;
138         }
139         if (!strcmp(error->string, ""))
140                 status = AUTH_GRANTED;
141         else
142                 status = AUTH_DENIED;
143
144 done:
145         json_delete(ctx->json);
146         return status;
147 }
148
149 static const authn_provider authn_subauth_provider = {
150         check_password,
151         NULL,   // get_realm_hash
152 };
153
154 static void
155 register_hooks(apr_pool_t *p)
156 {
157         ap_register_auth_provider(p,
158                 AUTHN_PROVIDER_GROUP,
159                 "subauth",
160                 AUTHN_PROVIDER_VERSION,
161                 &authn_subauth_provider,
162                 AP_AUTH_INTERNAL_PER_CONF);
163 }
164
165 /*** Configuration ***/
166
167 static void *
168 dir_create_config(apr_pool_t *p, char *dd UNUSED)
169 {
170         struct dir_config *dir = apr_pcalloc(p, sizeof(*dir));
171         return dir;
172 }
173
174 static void *
175 dir_merge_config(apr_pool_t *p, void *base, void *over)
176 {
177         struct dir_config *dir = apr_pcalloc(p, sizeof(*dir));
178         struct dir_config *b = base;
179         struct dir_config *o = over;
180         dir->socket = o->socket ? : b->socket;
181         dir->zone = o->zone ? : b->zone;
182         return dir;
183 }
184
185 static const char *
186 config_socket(cmd_parms *cmd UNUSED, void *d, const char *arg)
187 {
188         struct dir_config *dir = d;
189         dir->socket = arg;
190         return NULL;
191 }
192
193 static const char *
194 config_zone(cmd_parms *cmd UNUSED, void *d, const char *arg)
195 {
196         struct dir_config *dir = d;
197         dir->zone = arg;
198         return NULL;
199 }
200
201 static const command_rec cmds[] = {
202         AP_INIT_TAKE1("SubAuthSocket", config_socket, NULL, OR_AUTHCFG, "socket of subauth daemon"),
203         AP_INIT_TAKE1("SubAuthZone", config_zone, NULL, OR_AUTHCFG, "name of subauth zone"),
204         { NULL },
205 };
206
207 /*** Module ***/
208
209 AP_DECLARE_MODULE(authn_subauth) = {
210         STANDARD20_MODULE_STUFF,
211         dir_create_config,
212         dir_merge_config,
213         NULL, // srv_create_config,
214         NULL, // srv_merge_config,
215         cmds,
216         register_hooks,
217 };