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