Fix warnings
[mod_auth_gssapi.git] / src / mod_auth_gssapi.c
1 /*
2    MOD AUTH GSSAPI
3
4    Copyright (C) 2014 Simo Sorce <simo@redhat.com>
5
6    Permission is hereby granted, free of charge, to any person obtaining a
7    copy of this software and associated documentation files (the "Software"),
8    to deal in the Software without restriction, including without limitation
9    the rights to use, copy, modify, merge, publish, distribute, sublicense,
10    and/or sell copies of the Software, and to permit persons to whom the
11    Software is furnished to do so, subject to the following conditions:
12
13    The above copyright notice and this permission notice shall be included in
14    all copies or substantial portions of the Software.
15
16    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22    DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <stdbool.h>
26 #include <stdint.h>
27 #include <gssapi/gssapi.h>
28
29 #include <httpd.h>
30 #include <http_core.h>
31 #include <http_log.h>
32 #include <http_request.h>
33 #include <apr_strings.h>
34 #include <apr_base64.h>
35
36 module AP_MODULE_DECLARE_DATA mag_module;
37
38 struct mag_config {
39     bool ssl_only;
40     bool save_creds;
41 };
42
43 static char *mag_status(request_rec *req, int type, uint32_t err)
44 {
45     uint32_t maj_ret, min_ret;
46     gss_buffer_desc text;
47     uint32_t msg_ctx;
48     char *msg_ret;
49     int len;
50
51     msg_ret = NULL;
52     msg_ctx = 0;
53     do {
54         maj_ret = gss_display_status(&min_ret, err, type,
55                                      GSS_C_NO_OID, &msg_ctx, &text);
56         if (maj_ret != GSS_S_COMPLETE) {
57             return msg_ret;
58         }
59
60         len = text.length;
61         if (msg_ret) {
62             msg_ret = apr_psprintf(req->pool, "%s, %*s",
63                                    msg_ret, len, (char *)text.value);
64         } else {
65             msg_ret = apr_psprintf(req->pool, "%*s", len, (char *)text.value);
66         }
67         gss_release_buffer(&min_ret, &text);
68     } while (msg_ctx != 0);
69
70     return msg_ret;
71 }
72
73 static char *mag_error(request_rec *req, const char *msg,
74                        uint32_t maj, uint32_t min)
75 {
76     char *msg_maj;
77     char *msg_min;
78
79     msg_maj = mag_status(req, GSS_C_GSS_CODE, maj);
80     msg_min = mag_status(req, GSS_C_MECH_CODE, min);
81     return apr_psprintf(req->pool, "%s: [%s (%s)]", msg, msg_maj, msg_min);
82 }
83
84 static int mag_auth(request_rec *req)
85 {
86     const char *type;
87     struct mag_config *cfg;
88     const char *auth_header;
89     char *auth_header_type;
90     char *auth_header_value;
91     int ret = HTTP_UNAUTHORIZED;
92     gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
93     gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
94     gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
95     gss_buffer_desc name = GSS_C_EMPTY_BUFFER;
96     gss_name_t client = GSS_C_NO_NAME;
97     gss_cred_id_t delegated_cred = GSS_C_NO_CREDENTIAL;
98     uint32_t flags;
99     uint32_t maj, min;
100     char *reply;
101     size_t replen;
102
103     type = ap_auth_type(req);
104     if ((type == NULL) || (strcasecmp(type, "GSSAPI") != 0)) {
105         return DECLINED;
106     }
107
108     cfg = ap_get_module_config(req->per_dir_config, &mag_module);
109
110     if (cfg->ssl_only) {
111         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
112                       "FIXME: check for ssl!");
113     }
114
115     auth_header = apr_table_get(req->headers_in, "Authorization");
116     if (!auth_header) goto done;
117
118     auth_header_type = ap_getword_white(req->pool, &auth_header);
119     if (!auth_header_type) goto done;
120
121     if (strcasecmp(auth_header_type, "Negotiate") != 0) goto done;
122
123     auth_header_value = ap_getword_white(req->pool, &auth_header);
124     if (!auth_header_value) goto done;
125     input.length = apr_base64_decode_len(auth_header_value) + 1;
126     input.value = apr_pcalloc(req->pool, input.length);
127     if (!input.value) goto done;
128     input.length = apr_base64_decode(input.value, auth_header_value);
129
130     /* FIXME: this works only with "one-roundtrip" gssapi auth for now,
131      * should work with Krb, will fail with NTLMSSP */
132     maj = gss_accept_sec_context(&min, &ctx, GSS_C_NO_CREDENTIAL,
133                                  &input, GSS_C_NO_CHANNEL_BINDINGS,
134                                  &client, NULL, &output, &flags, NULL,
135                                  &delegated_cred);
136     if (GSS_ERROR(maj)) {
137         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
138                       mag_error(req, "gss_accept_sec_context() failed",
139                                 maj, min));
140         goto done;
141     }
142
143     if (output.length) {
144         replen = apr_base64_encode_len(output.length) + 1;
145         reply = apr_pcalloc(req->pool, 10 + replen);
146         if (!reply) goto done;
147         memcpy(reply, "Negotiate ", 10);
148         apr_base64_encode(&reply[10], output.value, output.length);
149         reply[replen] = '\0';
150         apr_table_add(req->err_headers_out, "WWW-Authenticate", reply);
151     }
152
153     maj = gss_display_name(&min, client, &name, NULL);
154     if (GSS_ERROR(maj)) {
155         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
156                       mag_error(req, "gss_accept_sec_context() failed",
157                                 maj, min));
158         goto done;
159     }
160
161     /* FIXME: save creds */
162
163     req->ap_auth_type = apr_pstrdup(req->pool, "Negotiate");
164     req->user = apr_pstrndup(req->pool, name.value, name.length);
165     ret = OK;
166
167 done:
168     if (ret == HTTP_UNAUTHORIZED) {
169         apr_table_add(req->err_headers_out, "WWW-Authenticate", "Negotiate");
170     }
171     gss_release_cred(&min, &delegated_cred);
172     gss_release_buffer(&min, &output);
173     gss_release_name(&min, &client);
174     gss_release_buffer(&min, &name);
175     gss_delete_sec_context(&min, &ctx, GSS_C_NO_BUFFER);
176     return ret;
177 }
178
179
180 static void *mag_create_dir_config(apr_pool_t *p, char *dir)
181 {
182     struct mag_config *cfg;
183
184     cfg = (struct mag_config *)apr_pcalloc(p, sizeof(struct mag_config));
185     if (!cfg) return NULL;
186
187     return cfg;
188 }
189
190 static const char *mag_ssl_only(cmd_parms *parms, void *mconfig, int on)
191 {
192     struct mag_config *cfg = (struct mag_config *)mconfig;
193     cfg->ssl_only = on ? true : false;
194     return NULL;
195 }
196
197 static const char *mag_save_creds(cmd_parms *parms, void *mconfig, int on)
198 {
199     struct mag_config *cfg = (struct mag_config *)mconfig;
200     cfg->save_creds = on ? true : false;
201     return NULL;
202 }
203
204 static const command_rec mag_commands[] = {
205     AP_INIT_FLAG("GSSSSLOnly", mag_ssl_only, NULL, OR_AUTHCFG,
206                   "Work only if connection is SSL Secured"),
207     AP_INIT_FLAG("GSSSaveCreds", mag_save_creds, NULL, OR_AUTHCFG,
208                   "Save credentials"),
209     { NULL }
210 };
211
212 static void
213 mag_register_hooks(apr_pool_t *p)
214 {
215     ap_hook_check_user_id(mag_auth, NULL, NULL, APR_HOOK_MIDDLE);
216 }
217
218 module AP_MODULE_DECLARE_DATA auth_gssapi_module =
219 {
220     STANDARD20_MODULE_STUFF,
221     mag_create_dir_config,
222     NULL,
223     NULL,
224     NULL,
225     mag_commands,
226     mag_register_hooks
227 };