1ea72e0fdf9c8cabc77e1b63ed6d552009a89bf3
[mod_auth_gssapi.git] / src / environ.c
1 /* Copyright (C) 2015 mod_auth_gssapi authors - See COPYING for (C) terms */
2
3 #include "mod_auth_gssapi.h"
4
5 struct name_attr {
6     gss_buffer_desc name;
7     int authenticated;
8     int complete;
9     gss_buffer_desc value;
10     gss_buffer_desc display_value;
11     const char *env_name;
12     int number;
13     int more;
14 };
15
16 static bool mag_get_name_attr(request_rec *req,
17                               gss_name_t name, struct name_attr *attr)
18 {
19     uint32_t maj, min;
20
21     maj = gss_get_name_attribute(&min, name, &attr->name,
22                                  &attr->authenticated,
23                                  &attr->complete,
24                                  &attr->value,
25                                  &attr->display_value,
26                                  &attr->more);
27     if (GSS_ERROR(maj)) {
28         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req,
29                       "gss_get_name_attribute() failed on %.*s%s",
30                       (int)attr->name.length, (char *)attr->name.value,
31                       mag_error(req, "", maj, min));
32         return false;
33     }
34
35     return true;
36 }
37
38 static void mc_add_name_attribute(struct mag_conn *mc,
39                                   const char *name, const char *value)
40 {
41     size_t size;
42
43     if (mc->na_count % 16 == 0) {
44         size = sizeof(struct mag_attr) * (mc->na_count + 16);
45         mc->name_attributes = realloc(mc->name_attributes, size);
46         if (!mc->name_attributes) apr_pool_abort_get(mc->pool)(ENOMEM);
47     }
48
49     mc->name_attributes[mc->na_count].name = apr_pstrdup(mc->pool, name);
50     mc->name_attributes[mc->na_count].value = apr_pstrdup(mc->pool, value);
51     mc->na_count++;
52 }
53
54 static void mag_set_env_name_attr(request_rec *req, struct mag_conn *mc,
55                                   struct name_attr *attr)
56 {
57     char *value = "";
58     int len = 0;
59
60     /* Prefer a display_value, otherwise fallback to value */
61     if (attr->display_value.length != 0) {
62         len = attr->display_value.length;
63         value = (char *)attr->display_value.value;
64     } else if (attr->value.length != 0) {
65         len = apr_base64_encode_len(attr->value.length);
66         value = apr_pcalloc(req->pool, len);
67         len = apr_base64_encode(value,
68                                 (char *)attr->value.value,
69                                 attr->value.length);
70     }
71
72     if (attr->number == 1) {
73         mc_add_name_attribute(mc,
74                               attr->env_name,
75                               apr_psprintf(req->pool, "%.*s", len, value));
76     }
77     if (attr->more != 0 || attr->number > 1) {
78         mc_add_name_attribute(mc,
79                               apr_psprintf(req->pool, "%s_%d",
80                                            attr->env_name, attr->number),
81                               apr_psprintf(req->pool, "%.*s", len, value));
82     }
83     if (attr->more == 0 && attr->number > 1) {
84         mc_add_name_attribute(mc,
85                               apr_psprintf(req->pool, "%s_N", attr->env_name),
86                               apr_psprintf(req->pool, "%d", attr->number - 1));
87     }
88 }
89
90 static void mag_add_json_name_attr(request_rec *req, bool first,
91                                    struct name_attr *attr, char **json)
92 {
93     const char *value = "";
94     int len = 0;
95     char *b64value = NULL;
96     int b64len = 0;
97     const char *vstart = "";
98     const char *vend = "";
99     const char *vformat;
100
101     if (attr->value.length != 0) {
102         b64len = apr_base64_encode_len(attr->value.length);
103         b64value = apr_pcalloc(req->pool, b64len);
104         b64len = apr_base64_encode(b64value,
105                                    (char *)attr->value.value,
106                                    attr->value.length);
107     }
108     if (attr->display_value.length != 0) {
109         len = attr->display_value.length;
110         value = (const char *)attr->display_value.value;
111     }
112     if (attr->number == 1) {
113         *json = apr_psprintf(req->pool,
114                             "%s%s\"%.*s\":{\"authenticated\":%s,"
115                                           "\"complete\":%s,"
116                                           "\"values\":[",
117                             *json, (first ? "" : ","),
118                             (int)attr->name.length, (char *)attr->name.value,
119                             attr->authenticated ? "true" : "false",
120                             attr->complete ? "true" : "false");
121     } else {
122         vstart = ",";
123     }
124
125     if (b64value) {
126         if (len) {
127             vformat = "%s%s{\"raw\":\"%s\",\"display\":\"%.*s\"}%s";
128         } else {
129             vformat = "%s%s{\"raw\":\"%s\",\"display\":%.*s}%s";
130         }
131     } else {
132         if (len) {
133             vformat = "%s%s{\"raw\":%s,\"display\":\"%.*s\"}%s";
134         } else {
135             vformat = "%s%s{\"raw\":%s,\"display\":%.*s}%s";
136         }
137     }
138
139     if (attr->more == 0) {
140         vend = "]}";
141     }
142
143     *json = apr_psprintf(req->pool, vformat, *json,
144                         vstart,
145                         b64value ? b64value : "null",
146                         len ? len : 4, len ? value : "null",
147                         vend);
148 }
149
150 gss_buffer_desc empty_buffer = GSS_C_EMPTY_BUFFER;
151
152 void mag_get_name_attributes(request_rec *req, struct mag_config *cfg,
153                              gss_name_t name, struct mag_conn *mc)
154 {
155     if (!cfg->name_attributes) {
156         return;
157     }
158
159     uint32_t maj, min;
160     gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET;
161     struct name_attr attr;
162     char *json = NULL;
163     char *error;
164     int count = 0;
165     int i, j;
166
167     maj = gss_inquire_name(&min, name, NULL, NULL, &attrs);
168     if (GSS_ERROR(maj)) {
169         error = mag_error(req, "gss_inquire_name() failed", maj, min);
170         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", error);
171         apr_table_set(req->subprocess_env, "GSS_NAME_ATTR_ERROR", error);
172         return;
173     }
174
175     if (!attrs || attrs->count == 0) {
176         mc_add_name_attribute(mc, "GSS_NAME_ATTR_ERROR", "0 attributes found");
177     }
178
179     if (cfg->name_attributes->output_json) {
180
181         if (attrs) count = attrs->count;
182
183         json = apr_psprintf(req->pool,
184                             "{\"name\":\"%s\",\"attributes\":{",
185                             mc->gss_name);
186     } else {
187         count = cfg->name_attributes->map_count;
188     }
189
190     for (i = 0; i < count; i++) {
191
192         memset(&attr, 0, sizeof(struct name_attr));
193
194         if (cfg->name_attributes->output_json) {
195             attr.name = attrs->elements[i];
196             for (j = 0; j < cfg->name_attributes->map_count; j++) {
197                 if (strncmp(cfg->name_attributes->map[j].attr_name,
198                             attrs->elements[i].value,
199                             attrs->elements[i].length) == 0) {
200                     attr.env_name = cfg->name_attributes->map[j].env_name;
201                     break;
202                 }
203             }
204         } else {
205             attr.name.length = strlen(cfg->name_attributes->map[i].attr_name);
206             attr.name.value = cfg->name_attributes->map[i].attr_name;
207             attr.env_name = cfg->name_attributes->map[i].env_name;
208         }
209
210         attr.number = 0;
211         attr.more = -1;
212         do {
213             attr.number++;
214             attr.value = empty_buffer;
215             attr.display_value = empty_buffer;
216
217             if (!mag_get_name_attr(req, name, &attr)) continue;
218
219             if (cfg->name_attributes->output_json) {
220                 mag_add_json_name_attr(req, i == 0, &attr, &json);
221             }
222             if (attr.env_name) {
223                 mag_set_env_name_attr(req, mc, &attr);
224             }
225
226             gss_release_buffer(&min, &attr.value);
227             gss_release_buffer(&min, &attr.display_value);
228         } while (attr.more != 0);
229     }
230
231     if (cfg->name_attributes->output_json) {
232         json = apr_psprintf(req->pool, "%s}}", json);
233         mc_add_name_attribute(mc, "GSS_NAME_ATTRS_JSON", json);
234     }
235 }
236
237 static void mag_set_name_attributes(request_rec *req, struct mag_conn *mc)
238 {
239     for (int i = 0; i < mc->na_count; i++) {
240         apr_table_set(req->subprocess_env,
241                       mc->name_attributes[i].name,
242                       mc->name_attributes[i].value);
243     }
244 }
245
246 static void mag_set_KRB5CCANME(request_rec *req, char *ccname)
247 {
248     apr_status_t status;
249     apr_finfo_t finfo;
250     char *value;
251
252     status = apr_stat(&finfo, ccname, APR_FINFO_MIN, req->pool);
253     if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
254         /* set the file cache anyway, but warn */
255         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, req,
256                       "KRB5CCNAME file (%s) lookup failed!", ccname);
257     }
258
259     value = apr_psprintf(req->pool, "FILE:%s", ccname);
260     apr_table_set(req->subprocess_env, "KRB5CCNAME", value);
261 }
262
263 void mag_set_req_data(request_rec *req,
264                       struct mag_config *cfg,
265                       struct mag_conn *mc)
266 {
267     apr_table_set(req->subprocess_env, "GSS_NAME", mc->gss_name);
268     apr_table_set(req->subprocess_env, "GSS_SESSION_EXPIRATION",
269                   apr_psprintf(req->pool,
270                                "%ld", (long)mc->expiration));
271     req->ap_auth_type = apr_pstrdup(req->pool,
272                                     mag_str_auth_type(mc->auth_type));
273     req->user = apr_pstrdup(req->pool, mc->user_name);
274
275     if (mc->name_attributes) {
276         mag_set_name_attributes(req, mc);
277     }
278
279 #ifdef HAVE_CRED_STORE
280     if (cfg->deleg_ccache_dir && mc->delegated) {
281         char *ccname;
282         ccname = mag_gss_name_to_ccache_name(req,
283                                              cfg->deleg_ccache_dir,
284                                              mc->gss_name);
285         if (ccname) {
286             mag_set_KRB5CCANME(req, ccname);
287         }
288     }
289 #endif
290 }