1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2014 - 2016, Steve Holme, <steve_holme@hotmail.com>.
9 * Copyright (C) 2015 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
10 *
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
13 * are also available at https://curl.haxx.se/docs/copyright.html.
14 *
15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 * copies of the Software, and permit persons to whom the Software is
17 * furnished to do so, under the terms of the COPYING file.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 * RFC2831 DIGEST-MD5 authentication
23 *
24 ***************************************************************************/
25
26 #include "curl_setup.h"
27
28 #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH)
29
30 #include <curl/curl.h>
31
32 #include "vauth/vauth.h"
33 #include "vauth/digest.h"
34 #include "urldata.h"
35 #include "curl_base64.h"
36 #include "warnless.h"
37 #include "curl_multibyte.h"
38 #include "sendf.h"
39 #include "strdup.h"
40 #include "strcase.h"
41
42 /* The last #include files should be: */
43 #include "curl_memory.h"
44 #include "memdebug.h"
45
46 /*
47 * Curl_auth_is_digest_supported()
48 *
49 * This is used to evaluate if DIGEST is supported.
50 *
51 * Parameters: None
52 *
53 * Returns TRUE if DIGEST is supported by Windows SSPI.
54 */
Curl_auth_is_digest_supported(void)55 bool Curl_auth_is_digest_supported(void)
56 {
57 PSecPkgInfo SecurityPackage;
58 SECURITY_STATUS status;
59
60 /* Query the security package for Digest */
61 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
62 &SecurityPackage);
63
64 /* Release the package buffer as it is not required anymore */
65 if(status == SEC_E_OK) {
66 s_pSecFn->FreeContextBuffer(SecurityPackage);
67 }
68
69 return (status == SEC_E_OK ? TRUE : FALSE);
70 }
71
72 /*
73 * Curl_auth_create_digest_md5_message()
74 *
75 * This is used to generate an already encoded DIGEST-MD5 response message
76 * ready for sending to the recipient.
77 *
78 * Parameters:
79 *
80 * data [in] - The session handle.
81 * chlg64 [in] - The base64 encoded challenge message.
82 * userp [in] - The user name in the format User or Domain\User.
83 * passwdp [in] - The user's password.
84 * service [in] - The service type such as http, smtp, pop or imap.
85 * outptr [in/out] - The address where a pointer to newly allocated memory
86 * holding the result will be stored upon completion.
87 * outlen [out] - The length of the output message.
88 *
89 * Returns CURLE_OK on success.
90 */
Curl_auth_create_digest_md5_message(struct Curl_easy * data,const char * chlg64,const char * userp,const char * passwdp,const char * service,char ** outptr,size_t * outlen)91 CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
92 const char *chlg64,
93 const char *userp,
94 const char *passwdp,
95 const char *service,
96 char **outptr, size_t *outlen)
97 {
98 CURLcode result = CURLE_OK;
99 TCHAR *spn = NULL;
100 size_t chlglen = 0;
101 size_t token_max = 0;
102 unsigned char *input_token = NULL;
103 unsigned char *output_token = NULL;
104 CredHandle credentials;
105 CtxtHandle context;
106 PSecPkgInfo SecurityPackage;
107 SEC_WINNT_AUTH_IDENTITY identity;
108 SEC_WINNT_AUTH_IDENTITY *p_identity;
109 SecBuffer chlg_buf;
110 SecBuffer resp_buf;
111 SecBufferDesc chlg_desc;
112 SecBufferDesc resp_desc;
113 SECURITY_STATUS status;
114 unsigned long attrs;
115 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
116
117 /* Decode the base-64 encoded challenge message */
118 if(strlen(chlg64) && *chlg64 != '=') {
119 result = Curl_base64_decode(chlg64, &input_token, &chlglen);
120 if(result)
121 return result;
122 }
123
124 /* Ensure we have a valid challenge message */
125 if(!input_token) {
126 infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
127
128 return CURLE_BAD_CONTENT_ENCODING;
129 }
130
131 /* Query the security package for DigestSSP */
132 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
133 &SecurityPackage);
134 if(status != SEC_E_OK) {
135 free(input_token);
136
137 failf(data, "SSPI: couldn't get auth info\n");
138 return CURLE_AUTH_ERROR;
139 }
140
141 token_max = SecurityPackage->cbMaxToken;
142
143 /* Release the package buffer as it is not required anymore */
144 s_pSecFn->FreeContextBuffer(SecurityPackage);
145
146 /* Allocate our response buffer */
147 output_token = malloc(token_max);
148 if(!output_token) {
149 free(input_token);
150
151 return CURLE_OUT_OF_MEMORY;
152 }
153
154 /* Generate our SPN */
155 spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
156 if(!spn) {
157 free(output_token);
158 free(input_token);
159
160 return CURLE_OUT_OF_MEMORY;
161 }
162
163 if(userp && *userp) {
164 /* Populate our identity structure */
165 result = Curl_create_sspi_identity(userp, passwdp, &identity);
166 if(result) {
167 free(spn);
168 free(output_token);
169 free(input_token);
170
171 return result;
172 }
173
174 /* Allow proper cleanup of the identity structure */
175 p_identity = &identity;
176 }
177 else
178 /* Use the current Windows user */
179 p_identity = NULL;
180
181 /* Acquire our credentials handle */
182 status = s_pSecFn->AcquireCredentialsHandle(NULL,
183 (TCHAR *) TEXT(SP_NAME_DIGEST),
184 SECPKG_CRED_OUTBOUND, NULL,
185 p_identity, NULL, NULL,
186 &credentials, &expiry);
187
188 if(status != SEC_E_OK) {
189 Curl_sspi_free_identity(p_identity);
190 free(spn);
191 free(output_token);
192 free(input_token);
193
194 return CURLE_LOGIN_DENIED;
195 }
196
197 /* Setup the challenge "input" security buffer */
198 chlg_desc.ulVersion = SECBUFFER_VERSION;
199 chlg_desc.cBuffers = 1;
200 chlg_desc.pBuffers = &chlg_buf;
201 chlg_buf.BufferType = SECBUFFER_TOKEN;
202 chlg_buf.pvBuffer = input_token;
203 chlg_buf.cbBuffer = curlx_uztoul(chlglen);
204
205 /* Setup the response "output" security buffer */
206 resp_desc.ulVersion = SECBUFFER_VERSION;
207 resp_desc.cBuffers = 1;
208 resp_desc.pBuffers = &resp_buf;
209 resp_buf.BufferType = SECBUFFER_TOKEN;
210 resp_buf.pvBuffer = output_token;
211 resp_buf.cbBuffer = curlx_uztoul(token_max);
212
213 /* Generate our response message */
214 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn,
215 0, 0, 0, &chlg_desc, 0,
216 &context, &resp_desc, &attrs,
217 &expiry);
218
219 if(status == SEC_I_COMPLETE_NEEDED ||
220 status == SEC_I_COMPLETE_AND_CONTINUE)
221 s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
222 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
223 s_pSecFn->FreeCredentialsHandle(&credentials);
224 Curl_sspi_free_identity(p_identity);
225 free(spn);
226 free(output_token);
227 free(input_token);
228
229 if(status == SEC_E_INSUFFICIENT_MEMORY)
230 return CURLE_OUT_OF_MEMORY;
231
232 return CURLE_AUTH_ERROR;
233 }
234
235 /* Base64 encode the response */
236 result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer,
237 outptr, outlen);
238
239 /* Free our handles */
240 s_pSecFn->DeleteSecurityContext(&context);
241 s_pSecFn->FreeCredentialsHandle(&credentials);
242
243 /* Free the identity structure */
244 Curl_sspi_free_identity(p_identity);
245
246 /* Free the SPN */
247 free(spn);
248
249 /* Free the response buffer */
250 free(output_token);
251
252 /* Free the decoded challenge message */
253 free(input_token);
254
255 return result;
256 }
257
258 /*
259 * Curl_override_sspi_http_realm()
260 *
261 * This is used to populate the domain in a SSPI identity structure
262 * The realm is extracted from the challenge message and used as the
263 * domain if it is not already explicitly set.
264 *
265 * Parameters:
266 *
267 * chlg [in] - The challenge message.
268 * identity [in/out] - The identity structure.
269 *
270 * Returns CURLE_OK on success.
271 */
Curl_override_sspi_http_realm(const char * chlg,SEC_WINNT_AUTH_IDENTITY * identity)272 CURLcode Curl_override_sspi_http_realm(const char *chlg,
273 SEC_WINNT_AUTH_IDENTITY *identity)
274 {
275 xcharp_u domain, dup_domain;
276
277 /* If domain is blank or unset, check challenge message for realm */
278 if(!identity->Domain || !identity->DomainLength) {
279 for(;;) {
280 char value[DIGEST_MAX_VALUE_LENGTH];
281 char content[DIGEST_MAX_CONTENT_LENGTH];
282
283 /* Pass all additional spaces here */
284 while(*chlg && ISSPACE(*chlg))
285 chlg++;
286
287 /* Extract a value=content pair */
288 if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
289 if(strcasecompare(value, "realm")) {
290
291 /* Setup identity's domain and length */
292 domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content);
293 if(!domain.tchar_ptr)
294 return CURLE_OUT_OF_MEMORY;
295
296 dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr);
297 if(!dup_domain.tchar_ptr) {
298 curlx_unicodefree(domain.tchar_ptr);
299 return CURLE_OUT_OF_MEMORY;
300 }
301
302 free(identity->Domain);
303 identity->Domain = dup_domain.tbyte_ptr;
304 identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr));
305 dup_domain.tchar_ptr = NULL;
306
307 curlx_unicodefree(domain.tchar_ptr);
308 }
309 else {
310 /* Unknown specifier, ignore it! */
311 }
312 }
313 else
314 break; /* We're done here */
315
316 /* Pass all additional spaces here */
317 while(*chlg && ISSPACE(*chlg))
318 chlg++;
319
320 /* Allow the list to be comma-separated */
321 if(',' == *chlg)
322 chlg++;
323 }
324 }
325
326 return CURLE_OK;
327 }
328
329 /*
330 * Curl_auth_decode_digest_http_message()
331 *
332 * This is used to decode a HTTP DIGEST challenge message into the separate
333 * attributes.
334 *
335 * Parameters:
336 *
337 * chlg [in] - The challenge message.
338 * digest [in/out] - The digest data struct being used and modified.
339 *
340 * Returns CURLE_OK on success.
341 */
Curl_auth_decode_digest_http_message(const char * chlg,struct digestdata * digest)342 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
343 struct digestdata *digest)
344 {
345 size_t chlglen = strlen(chlg);
346
347 /* We had an input token before so if there's another one now that means we
348 provided bad credentials in the previous request or it's stale. */
349 if(digest->input_token) {
350 bool stale = false;
351 const char *p = chlg;
352
353 /* Check for the 'stale' directive */
354 for(;;) {
355 char value[DIGEST_MAX_VALUE_LENGTH];
356 char content[DIGEST_MAX_CONTENT_LENGTH];
357
358 while(*p && ISSPACE(*p))
359 p++;
360
361 if(!Curl_auth_digest_get_pair(p, value, content, &p))
362 break;
363
364 if(strcasecompare(value, "stale") &&
365 strcasecompare(content, "true")) {
366 stale = true;
367 break;
368 }
369
370 while(*p && ISSPACE(*p))
371 p++;
372
373 if(',' == *p)
374 p++;
375 }
376
377 if(stale)
378 Curl_auth_digest_cleanup(digest);
379 else
380 return CURLE_LOGIN_DENIED;
381 }
382
383 /* Store the challenge for use later */
384 digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen + 1);
385 if(!digest->input_token)
386 return CURLE_OUT_OF_MEMORY;
387
388 digest->input_token_len = chlglen;
389
390 return CURLE_OK;
391 }
392
393 /*
394 * Curl_auth_create_digest_http_message()
395 *
396 * This is used to generate a HTTP DIGEST response message ready for sending
397 * to the recipient.
398 *
399 * Parameters:
400 *
401 * data [in] - The session handle.
402 * userp [in] - The user name in the format User or Domain\User.
403 * passwdp [in] - The user's password.
404 * request [in] - The HTTP request.
405 * uripath [in] - The path of the HTTP uri.
406 * digest [in/out] - The digest data struct being used and modified.
407 * outptr [in/out] - The address where a pointer to newly allocated memory
408 * holding the result will be stored upon completion.
409 * outlen [out] - The length of the output message.
410 *
411 * Returns CURLE_OK on success.
412 */
Curl_auth_create_digest_http_message(struct Curl_easy * data,const char * userp,const char * passwdp,const unsigned char * request,const unsigned char * uripath,struct digestdata * digest,char ** outptr,size_t * outlen)413 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
414 const char *userp,
415 const char *passwdp,
416 const unsigned char *request,
417 const unsigned char *uripath,
418 struct digestdata *digest,
419 char **outptr, size_t *outlen)
420 {
421 size_t token_max;
422 char *resp;
423 BYTE *output_token;
424 size_t output_token_len = 0;
425 PSecPkgInfo SecurityPackage;
426 SecBuffer chlg_buf[5];
427 SecBufferDesc chlg_desc;
428 SECURITY_STATUS status;
429
430 (void) data;
431
432 /* Query the security package for DigestSSP */
433 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
434 &SecurityPackage);
435 if(status != SEC_E_OK) {
436 failf(data, "SSPI: couldn't get auth info\n");
437 return CURLE_AUTH_ERROR;
438 }
439
440 token_max = SecurityPackage->cbMaxToken;
441
442 /* Release the package buffer as it is not required anymore */
443 s_pSecFn->FreeContextBuffer(SecurityPackage);
444
445 /* Allocate the output buffer according to the max token size as indicated
446 by the security package */
447 output_token = malloc(token_max);
448 if(!output_token) {
449 return CURLE_OUT_OF_MEMORY;
450 }
451
452 /* If the user/passwd that was used to make the identity for http_context
453 has changed then delete that context. */
454 if((userp && !digest->user) || (!userp && digest->user) ||
455 (passwdp && !digest->passwd) || (!passwdp && digest->passwd) ||
456 (userp && digest->user && strcmp(userp, digest->user)) ||
457 (passwdp && digest->passwd && strcmp(passwdp, digest->passwd))) {
458 if(digest->http_context) {
459 s_pSecFn->DeleteSecurityContext(digest->http_context);
460 Curl_safefree(digest->http_context);
461 }
462 Curl_safefree(digest->user);
463 Curl_safefree(digest->passwd);
464 }
465
466 if(digest->http_context) {
467 chlg_desc.ulVersion = SECBUFFER_VERSION;
468 chlg_desc.cBuffers = 5;
469 chlg_desc.pBuffers = chlg_buf;
470 chlg_buf[0].BufferType = SECBUFFER_TOKEN;
471 chlg_buf[0].pvBuffer = NULL;
472 chlg_buf[0].cbBuffer = 0;
473 chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
474 chlg_buf[1].pvBuffer = (void *) request;
475 chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
476 chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
477 chlg_buf[2].pvBuffer = (void *) uripath;
478 chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath));
479 chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
480 chlg_buf[3].pvBuffer = NULL;
481 chlg_buf[3].cbBuffer = 0;
482 chlg_buf[4].BufferType = SECBUFFER_PADDING;
483 chlg_buf[4].pvBuffer = output_token;
484 chlg_buf[4].cbBuffer = curlx_uztoul(token_max);
485
486 status = s_pSecFn->MakeSignature(digest->http_context, 0, &chlg_desc, 0);
487 if(status == SEC_E_OK)
488 output_token_len = chlg_buf[4].cbBuffer;
489 else { /* delete the context so a new one can be made */
490 infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx\n",
491 (long)status);
492 s_pSecFn->DeleteSecurityContext(digest->http_context);
493 Curl_safefree(digest->http_context);
494 }
495 }
496
497 if(!digest->http_context) {
498 CredHandle credentials;
499 SEC_WINNT_AUTH_IDENTITY identity;
500 SEC_WINNT_AUTH_IDENTITY *p_identity;
501 SecBuffer resp_buf;
502 SecBufferDesc resp_desc;
503 unsigned long attrs;
504 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
505 TCHAR *spn;
506
507 /* free the copy of user/passwd used to make the previous identity */
508 Curl_safefree(digest->user);
509 Curl_safefree(digest->passwd);
510
511 if(userp && *userp) {
512 /* Populate our identity structure */
513 if(Curl_create_sspi_identity(userp, passwdp, &identity)) {
514 free(output_token);
515 return CURLE_OUT_OF_MEMORY;
516 }
517
518 /* Populate our identity domain */
519 if(Curl_override_sspi_http_realm((const char *) digest->input_token,
520 &identity)) {
521 free(output_token);
522 return CURLE_OUT_OF_MEMORY;
523 }
524
525 /* Allow proper cleanup of the identity structure */
526 p_identity = &identity;
527 }
528 else
529 /* Use the current Windows user */
530 p_identity = NULL;
531
532 if(userp) {
533 digest->user = strdup(userp);
534
535 if(!digest->user) {
536 free(output_token);
537 return CURLE_OUT_OF_MEMORY;
538 }
539 }
540
541 if(passwdp) {
542 digest->passwd = strdup(passwdp);
543
544 if(!digest->passwd) {
545 free(output_token);
546 Curl_safefree(digest->user);
547 return CURLE_OUT_OF_MEMORY;
548 }
549 }
550
551 /* Acquire our credentials handle */
552 status = s_pSecFn->AcquireCredentialsHandle(NULL,
553 (TCHAR *) TEXT(SP_NAME_DIGEST),
554 SECPKG_CRED_OUTBOUND, NULL,
555 p_identity, NULL, NULL,
556 &credentials, &expiry);
557 if(status != SEC_E_OK) {
558 Curl_sspi_free_identity(p_identity);
559 free(output_token);
560
561 return CURLE_LOGIN_DENIED;
562 }
563
564 /* Setup the challenge "input" security buffer if present */
565 chlg_desc.ulVersion = SECBUFFER_VERSION;
566 chlg_desc.cBuffers = 3;
567 chlg_desc.pBuffers = chlg_buf;
568 chlg_buf[0].BufferType = SECBUFFER_TOKEN;
569 chlg_buf[0].pvBuffer = digest->input_token;
570 chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
571 chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
572 chlg_buf[1].pvBuffer = (void *) request;
573 chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
574 chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
575 chlg_buf[2].pvBuffer = NULL;
576 chlg_buf[2].cbBuffer = 0;
577
578 /* Setup the response "output" security buffer */
579 resp_desc.ulVersion = SECBUFFER_VERSION;
580 resp_desc.cBuffers = 1;
581 resp_desc.pBuffers = &resp_buf;
582 resp_buf.BufferType = SECBUFFER_TOKEN;
583 resp_buf.pvBuffer = output_token;
584 resp_buf.cbBuffer = curlx_uztoul(token_max);
585
586 spn = curlx_convert_UTF8_to_tchar((char *) uripath);
587 if(!spn) {
588 s_pSecFn->FreeCredentialsHandle(&credentials);
589
590 Curl_sspi_free_identity(p_identity);
591 free(output_token);
592
593 return CURLE_OUT_OF_MEMORY;
594 }
595
596 /* Allocate our new context handle */
597 digest->http_context = calloc(1, sizeof(CtxtHandle));
598 if(!digest->http_context)
599 return CURLE_OUT_OF_MEMORY;
600
601 /* Generate our response message */
602 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL,
603 spn,
604 ISC_REQ_USE_HTTP_STYLE, 0, 0,
605 &chlg_desc, 0,
606 digest->http_context,
607 &resp_desc, &attrs, &expiry);
608 curlx_unicodefree(spn);
609
610 if(status == SEC_I_COMPLETE_NEEDED ||
611 status == SEC_I_COMPLETE_AND_CONTINUE)
612 s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
613 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
614 s_pSecFn->FreeCredentialsHandle(&credentials);
615
616 Curl_sspi_free_identity(p_identity);
617 free(output_token);
618
619 Curl_safefree(digest->http_context);
620
621 if(status == SEC_E_INSUFFICIENT_MEMORY)
622 return CURLE_OUT_OF_MEMORY;
623
624 return CURLE_AUTH_ERROR;
625 }
626
627 output_token_len = resp_buf.cbBuffer;
628
629 s_pSecFn->FreeCredentialsHandle(&credentials);
630 Curl_sspi_free_identity(p_identity);
631 }
632
633 resp = malloc(output_token_len + 1);
634 if(!resp) {
635 free(output_token);
636
637 return CURLE_OUT_OF_MEMORY;
638 }
639
640 /* Copy the generated response */
641 memcpy(resp, output_token, output_token_len);
642 resp[output_token_len] = 0;
643
644 /* Return the response */
645 *outptr = resp;
646 *outlen = output_token_len;
647
648 /* Free the response buffer */
649 free(output_token);
650
651 return CURLE_OK;
652 }
653
654 /*
655 * Curl_auth_digest_cleanup()
656 *
657 * This is used to clean up the digest specific data.
658 *
659 * Parameters:
660 *
661 * digest [in/out] - The digest data struct being cleaned up.
662 *
663 */
Curl_auth_digest_cleanup(struct digestdata * digest)664 void Curl_auth_digest_cleanup(struct digestdata *digest)
665 {
666 /* Free the input token */
667 Curl_safefree(digest->input_token);
668
669 /* Reset any variables */
670 digest->input_token_len = 0;
671
672 /* Delete security context */
673 if(digest->http_context) {
674 s_pSecFn->DeleteSecurityContext(digest->http_context);
675 Curl_safefree(digest->http_context);
676 }
677
678 /* Free the copy of user/passwd used to make the identity for http_context */
679 Curl_safefree(digest->user);
680 Curl_safefree(digest->passwd);
681 }
682
683 #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */
684