]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/serf/auth/auth_spnego_sspi.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / serf / auth / auth_spnego_sspi.c
1 /* Copyright 2010 Justin Erenkrantz and Greg Stein
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include "auth_spnego.h"
17 #include "serf.h"
18 #include "serf_private.h"
19
20 #ifdef SERF_USE_SSPI
21 #include <apr.h>
22 #include <apr_strings.h>
23
24 #define SECURITY_WIN32
25 #include <sspi.h>
26
27 /* SEC_E_MUTUAL_AUTH_FAILED is not defined in Windows Platform SDK 5.0. */
28 #ifndef SEC_E_MUTUAL_AUTH_FAILED
29 #define SEC_E_MUTUAL_AUTH_FAILED _HRESULT_TYPEDEF_(0x80090363L)
30 #endif
31
32 struct serf__spnego_context_t
33 {
34     CredHandle sspi_credentials;
35     CtxtHandle sspi_context;
36     BOOL initalized;
37     apr_pool_t *pool;
38
39     /* Service Principal Name (SPN) used for authentication. */
40     const char *target_name;
41
42     /* One of SERF_AUTHN_* authentication types.*/
43     int authn_type;
44 };
45
46 /* Map SECURITY_STATUS from SSPI to APR error code. Some error codes mapped
47  * to our own codes and some to Win32 error codes:
48  * http://support.microsoft.com/kb/113996
49  */
50 static apr_status_t
51 map_sspi_status(SECURITY_STATUS sspi_status)
52 {
53     switch(sspi_status)
54     {
55     case SEC_E_INSUFFICIENT_MEMORY:
56         return APR_FROM_OS_ERROR(ERROR_NO_SYSTEM_RESOURCES);
57     case SEC_E_INVALID_HANDLE:
58         return APR_FROM_OS_ERROR(ERROR_INVALID_HANDLE);
59     case SEC_E_UNSUPPORTED_FUNCTION:
60         return APR_FROM_OS_ERROR(ERROR_INVALID_FUNCTION);
61     case SEC_E_TARGET_UNKNOWN:
62         return APR_FROM_OS_ERROR(ERROR_BAD_NETPATH);
63     case SEC_E_INTERNAL_ERROR:
64         return APR_FROM_OS_ERROR(ERROR_INTERNAL_ERROR);
65     case SEC_E_SECPKG_NOT_FOUND:
66     case SEC_E_BAD_PKGID:
67         return APR_FROM_OS_ERROR(ERROR_NO_SUCH_PACKAGE);
68     case SEC_E_NO_IMPERSONATION:
69         return APR_FROM_OS_ERROR(ERROR_CANNOT_IMPERSONATE);
70     case SEC_E_NO_AUTHENTICATING_AUTHORITY:
71         return APR_FROM_OS_ERROR(ERROR_NO_LOGON_SERVERS);
72     case SEC_E_UNTRUSTED_ROOT:
73         return APR_FROM_OS_ERROR(ERROR_TRUST_FAILURE);
74     case SEC_E_WRONG_PRINCIPAL:
75         return APR_FROM_OS_ERROR(ERROR_WRONG_TARGET_NAME);
76     case SEC_E_MUTUAL_AUTH_FAILED:
77         return APR_FROM_OS_ERROR(ERROR_MUTUAL_AUTH_FAILED);
78     case SEC_E_TIME_SKEW:
79         return APR_FROM_OS_ERROR(ERROR_TIME_SKEW);
80     default:
81         return SERF_ERROR_AUTHN_FAILED;
82     }
83 }
84
85 /* Cleans the SSPI context object, when the pool used to create it gets
86    cleared or destroyed. */
87 static apr_status_t
88 cleanup_ctx(void *data)
89 {
90     serf__spnego_context_t *ctx = data;
91
92     if (SecIsValidHandle(&ctx->sspi_context)) {
93         DeleteSecurityContext(&ctx->sspi_context);
94         SecInvalidateHandle(&ctx->sspi_context);
95     }
96
97     if (SecIsValidHandle(&ctx->sspi_credentials)) {
98         FreeCredentialsHandle(&ctx->sspi_context);
99         SecInvalidateHandle(&ctx->sspi_context);
100     }
101
102     return APR_SUCCESS;
103 }
104
105 static apr_status_t
106 cleanup_sec_buffer(void *data)
107 {
108     FreeContextBuffer(data);
109
110     return APR_SUCCESS;
111 }
112
113 apr_status_t
114 serf__spnego_create_sec_context(serf__spnego_context_t **ctx_p,
115                                 const serf__authn_scheme_t *scheme,
116                                 apr_pool_t *result_pool,
117                                 apr_pool_t *scratch_pool)
118 {
119     SECURITY_STATUS sspi_status;
120     serf__spnego_context_t *ctx;
121     const char *sspi_package;
122
123     ctx = apr_pcalloc(result_pool, sizeof(*ctx));
124
125     SecInvalidateHandle(&ctx->sspi_context);
126     SecInvalidateHandle(&ctx->sspi_credentials);
127     ctx->initalized = FALSE;
128     ctx->pool = result_pool;
129     ctx->target_name = NULL;
130     ctx->authn_type = scheme->type;
131
132     apr_pool_cleanup_register(result_pool, ctx,
133                               cleanup_ctx,
134                               apr_pool_cleanup_null);
135
136     if (ctx->authn_type == SERF_AUTHN_NEGOTIATE)
137         sspi_package = "Negotiate";
138     else
139         sspi_package = "NTLM";
140
141     sspi_status = AcquireCredentialsHandle(
142         NULL, sspi_package, SECPKG_CRED_OUTBOUND,
143         NULL, NULL, NULL, NULL,
144         &ctx->sspi_credentials, NULL);
145
146     if (FAILED(sspi_status)) {
147         return map_sspi_status(sspi_status);
148     }
149
150     *ctx_p = ctx;
151
152     return APR_SUCCESS;
153 }
154
155 static apr_status_t
156 get_canonical_hostname(const char **canonname,
157                        const char *hostname,
158                        apr_pool_t *pool)
159 {
160     struct addrinfo hints;
161     struct addrinfo *addrinfo;
162
163     ZeroMemory(&hints, sizeof(hints));
164     hints.ai_flags = AI_CANONNAME;
165
166     if (getaddrinfo(hostname, NULL, &hints, &addrinfo)) {
167         return apr_get_netos_error();
168     }
169
170     if (addrinfo) {
171         *canonname = apr_pstrdup(pool, addrinfo->ai_canonname);
172     }
173     else {
174         *canonname = apr_pstrdup(pool, hostname);
175     }
176
177     freeaddrinfo(addrinfo);
178     return APR_SUCCESS;
179 }
180
181 apr_status_t
182 serf__spnego_reset_sec_context(serf__spnego_context_t *ctx)
183 {
184     if (SecIsValidHandle(&ctx->sspi_context)) {
185         DeleteSecurityContext(&ctx->sspi_context);
186         SecInvalidateHandle(&ctx->sspi_context);
187     }
188
189     ctx->initalized = FALSE;
190
191     return APR_SUCCESS;
192 }
193
194 apr_status_t
195 serf__spnego_init_sec_context(serf_connection_t *conn,
196                               serf__spnego_context_t *ctx,
197                               const char *service,
198                               const char *hostname,
199                               serf__spnego_buffer_t *input_buf,
200                               serf__spnego_buffer_t *output_buf,
201                               apr_pool_t *result_pool,
202                               apr_pool_t *scratch_pool
203                               )
204 {
205     SECURITY_STATUS status;
206     ULONG actual_attr;
207     SecBuffer sspi_in_buffer;
208     SecBufferDesc sspi_in_buffer_desc;
209     SecBuffer sspi_out_buffer;
210     SecBufferDesc sspi_out_buffer_desc;
211     apr_status_t apr_status;
212     const char *canonname;
213
214     if (!ctx->initalized && ctx->authn_type == SERF_AUTHN_NEGOTIATE) {
215         apr_status = get_canonical_hostname(&canonname, hostname, scratch_pool);
216         if (apr_status) {
217             return apr_status;
218         }
219
220         ctx->target_name = apr_pstrcat(scratch_pool, service, "/", canonname,
221                                        NULL);
222
223         serf__log_skt(AUTH_VERBOSE, __FILE__, conn->skt,
224                       "Using SPN '%s' for '%s'\n", ctx->target_name, hostname);
225     }
226     else if (ctx->authn_type == SERF_AUTHN_NTLM)
227     {
228         /* Target name is not used for NTLM authentication. */
229         ctx->target_name = NULL;
230     }
231
232     /* Prepare input buffer description. */
233     sspi_in_buffer.BufferType = SECBUFFER_TOKEN;
234     sspi_in_buffer.pvBuffer = input_buf->value;
235     sspi_in_buffer.cbBuffer = input_buf->length; 
236
237     sspi_in_buffer_desc.cBuffers = 1;
238     sspi_in_buffer_desc.pBuffers = &sspi_in_buffer;
239     sspi_in_buffer_desc.ulVersion = SECBUFFER_VERSION;
240
241     /* Output buffers. Output buffer will be allocated by system. */
242     sspi_out_buffer.BufferType = SECBUFFER_TOKEN;
243     sspi_out_buffer.pvBuffer = NULL; 
244     sspi_out_buffer.cbBuffer = 0;
245
246     sspi_out_buffer_desc.cBuffers = 1;
247     sspi_out_buffer_desc.pBuffers = &sspi_out_buffer;
248     sspi_out_buffer_desc.ulVersion = SECBUFFER_VERSION;
249
250     status = InitializeSecurityContext(
251         &ctx->sspi_credentials,
252         ctx->initalized ? &ctx->sspi_context : NULL,
253         ctx->target_name,
254         ISC_REQ_ALLOCATE_MEMORY
255         | ISC_REQ_MUTUAL_AUTH
256         | ISC_REQ_CONFIDENTIALITY,
257         0,                          /* Reserved1 */
258         SECURITY_NETWORK_DREP,
259         &sspi_in_buffer_desc,
260         0,                          /* Reserved2 */
261         &ctx->sspi_context,
262         &sspi_out_buffer_desc,
263         &actual_attr,
264         NULL);
265
266     if (sspi_out_buffer.cbBuffer > 0) {
267         apr_pool_cleanup_register(result_pool, sspi_out_buffer.pvBuffer,
268                                   cleanup_sec_buffer,
269                                   apr_pool_cleanup_null);
270     }
271
272     ctx->initalized = TRUE;
273
274     /* Finish authentication if SSPI requires so. */
275     if (status == SEC_I_COMPLETE_NEEDED
276         || status == SEC_I_COMPLETE_AND_CONTINUE)
277     {
278         CompleteAuthToken(&ctx->sspi_context, &sspi_out_buffer_desc);
279     }
280
281     output_buf->value = sspi_out_buffer.pvBuffer;
282     output_buf->length = sspi_out_buffer.cbBuffer;
283
284     switch(status) {
285     case SEC_I_COMPLETE_AND_CONTINUE:
286     case SEC_I_CONTINUE_NEEDED:
287         return APR_EAGAIN;
288
289     case SEC_I_COMPLETE_NEEDED:
290     case SEC_E_OK:
291         return APR_SUCCESS;
292
293     default:
294         return map_sspi_status(status);
295     }
296 }
297
298 #endif /* SERF_USE_SSPI */