importing current version of mod_auth_gssapi
[mod_auth_kerb.git] / mod_auth_gssapi.c
1 #include "mod_auth_gssapi.h"
2
3 module AP_MODULE_DECLARE_DATA auth_gssapi_module;
4
5 #define command(name, func, var, type, usage)           \
6   AP_INIT_ ## type (name, (void*) func,                 \
7         (void*)APR_OFFSETOF(gss_auth_config, var),      \
8         OR_AUTHCFG | RSRC_CONF, usage)
9
10 static const command_rec gss_config_cmds[] = {
11     command("GSSServiceName", ap_set_string_slot, service_name,
12             TAKE1, "Service name used for Apache authentication."),
13
14     command("GSSKrb5Keytab", ap_set_string_slot, krb5_keytab,
15             TAKE1, "Location of Kerberos V5 keytab file."),
16
17     { NULL }
18 };
19
20 static void *
21 gss_config_dir_create(apr_pool_t *p, char *d)
22 {
23     gss_auth_config *conf;
24
25     conf = (gss_auth_config *) apr_pcalloc(p, sizeof(*conf));
26     return conf;
27 }
28
29 void
30 gss_log(const char *file, int line, int level, int status,
31         const request_rec *r, const char *fmt, ...)
32 {
33     char errstr[1024];
34     va_list ap;
35
36     va_start(ap, fmt);
37     vsnprintf(errstr, sizeof(errstr), fmt, ap);
38     va_end(ap);
39    
40     ap_log_rerror(file, line, level | APLOG_NOERRNO, status, r, "%s", errstr);
41 }
42
43 static void
44 set_http_headers(request_rec *r, const gss_auth_config *conf,
45                  char *negotiate_ret_value)
46 {
47     char *negoauth_param;
48     const char *header_name = (r->proxyreq == PROXYREQ_PROXY) ?
49         "Proxy-Authenticate" : "WWW-Authenticate";
50
51     if (negotiate_ret_value == NULL)
52         return;
53
54     negoauth_param = (*negotiate_ret_value == '\0') ? "GSSAPI" :
55         apr_pstrcat(r->pool, "GSSAPI ", negotiate_ret_value, NULL);
56     apr_table_add(r->err_headers_out, header_name, negoauth_param);
57 }
58
59 static apr_status_t
60 cleanup_conn_ctx(void *data)
61 {
62     gss_conn_ctx ctx = (gss_conn_ctx) data;
63     OM_uint32 minor_status;
64
65     if (ctx && ctx->context != GSS_C_NO_CONTEXT)
66         gss_delete_sec_context(&minor_status, &ctx->context, GSS_C_NO_BUFFER);
67
68     return APR_SUCCESS;
69 }
70
71 static gss_conn_ctx
72 gss_get_conn_ctx(request_rec *r)
73 {
74     char key[1024];
75     gss_conn_ctx ctx = NULL;
76
77     snprintf(key, sizeof(key), "mod_auth_gssapi:conn_ctx");
78     apr_pool_userdata_get((void **)&ctx, key, r->connection->pool);
79     /* XXX LOG */
80     if (ctx == NULL) {
81         ctx = (gss_conn_ctx) apr_palloc(r->connection->pool, sizeof(*ctx));
82         if (ctx == NULL)
83             return NULL;
84         ctx->context = GSS_C_NO_CONTEXT;
85         ctx->state = GSS_CTX_EMPTY;
86         ctx->user = NULL;
87         apr_pool_userdata_set(ctx, key, cleanup_conn_ctx, r->connection->pool);
88     }
89     return ctx;
90 }
91
92 static int
93 gss_authenticate_user(request_rec *r)
94 {
95     gss_auth_config *conf = 
96         (gss_auth_config *) ap_get_module_config(r->per_dir_config,
97                                                 &auth_gssapi_module);
98     const char *auth_line = NULL;
99     const char *type = NULL;
100     char *auth_type = NULL;
101     char *negotiate_ret_value = NULL;
102     gss_conn_ctx conn_ctx = NULL;
103     int ret;
104
105     gss_log(APLOG_MARK, APLOG_DEBUG, 0, r, "Entering GSSAPI authentication");
106    
107     /* get the type specified in Apache configuration */
108     type = ap_auth_type(r);
109     if (type == NULL || strcmp(type, "GSSAPI") != 0) {
110         gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
111                 "AuthType '%s' is not for us, bailing out",
112                 (type) ? type : "(NULL)");
113
114         return DECLINED;
115     }
116
117     /* get what the user sent us in the HTTP header */
118     auth_line = apr_table_get(r->headers_in, (r->proxyreq == PROXYREQ_PROXY)
119                                             ? "Proxy-Authorization"
120                                             : "Authorization");
121     if (auth_line == NULL) {
122         gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
123                 "Client hasn't sent any authentication data, giving up");
124         set_http_headers(r, conf, "\0");
125         return HTTP_UNAUTHORIZED;
126     }
127
128     auth_type = ap_getword_white(r->pool, &auth_line);
129     if (strcasecmp(auth_type, "GSSAPI") != 0) {
130         gss_log(APLOG_MARK, APLOG_DEBUG, 0, r,
131                 "Unsupported authentication type (%s) requested by client",
132                 (auth_type) ? auth_type : "(NULL)");
133         set_http_headers(r, conf, "\0");
134         return HTTP_UNAUTHORIZED;
135     }
136
137     conn_ctx = gss_get_conn_ctx(r);
138     if (conn_ctx == NULL) {
139         gss_log(APLOG_MARK, APLOG_ERR, 0, r,
140                 "Failed to create internal context: probably not enough memory");
141         return HTTP_INTERNAL_SERVER_ERROR;
142     }
143
144     /* optimizing hack */
145     if (conn_ctx->state == GSS_CTX_ESTABLISHED && auth_line == NULL) {
146         r->user = apr_pstrdup(r->pool, conn_ctx->user);
147         r->ap_auth_type = "GSSAPI";
148         return OK;
149     }
150
151     /* XXXX subrequests ignored, only successful accesses taken into account! */
152     if (!ap_is_initial_req(r) && conn_ctx->state == GSS_CTX_ESTABLISHED) {
153         r->user = apr_pstrdup(r->pool, conn_ctx->user);
154         r->ap_auth_type = "GSSAPI";
155         return OK;
156     }
157
158     ret = gss_authenticate(r, conf, conn_ctx,
159                            auth_line, &negotiate_ret_value);
160     if (ret == HTTP_UNAUTHORIZED || ret == OK) {
161         /* LOG?? */
162         set_http_headers(r, conf, negotiate_ret_value);
163     }
164
165     if (ret == OK) {
166         r->user = apr_pstrdup(r->pool, conn_ctx->user);
167         r->ap_auth_type = "GSSAPI";
168     }
169
170     /* debug LOG ??? */
171
172     return ret;
173 }
174
175 static void
176 gss_register_hooks(apr_pool_t *p)
177 {
178     ap_hook_check_user_id(gss_authenticate_user, NULL, NULL, APR_HOOK_MIDDLE);
179 }
180
181 module AP_MODULE_DECLARE_DATA auth_gssapi_module = {
182     STANDARD20_MODULE_STUFF,
183     gss_config_dir_create,
184     NULL,
185     NULL,
186     NULL,
187     gss_config_cmds,
188     gss_register_hooks
189 };