1 /* 2 * Copyright 2005 Kai Blin 3 * Copyright 2012 Hans Leidekker for CodeWeavers 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 * 19 * This file implements a Negotiate provider that simply forwards to 20 * the NTLM provider. 21 */ 22 23 #include "precomp.h" 24 25 #include <wine/debug.h> 26 WINE_DEFAULT_DEBUG_CHANNEL(secur32); 27 28 /*********************************************************************** 29 * QueryCredentialsAttributesA 30 */ 31 static SECURITY_STATUS SEC_ENTRY nego_QueryCredentialsAttributesA( 32 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer) 33 { 34 FIXME("%p, %u, %p\n", phCredential, ulAttribute, pBuffer); 35 return SEC_E_UNSUPPORTED_FUNCTION; 36 } 37 38 /*********************************************************************** 39 * QueryCredentialsAttributesW 40 */ 41 static SECURITY_STATUS SEC_ENTRY nego_QueryCredentialsAttributesW( 42 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer) 43 { 44 FIXME("%p, %u, %p\n", phCredential, ulAttribute, pBuffer); 45 return SEC_E_UNSUPPORTED_FUNCTION; 46 } 47 48 /*********************************************************************** 49 * AcquireCredentialsHandleW 50 */ 51 static SECURITY_STATUS SEC_ENTRY nego_AcquireCredentialsHandleW( 52 SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse, 53 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn, 54 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry ) 55 { 56 static SEC_WCHAR ntlmW[] = {'N','T','L','M',0}; 57 SECURITY_STATUS ret; 58 59 TRACE("%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p\n", 60 debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse, 61 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); 62 63 FIXME("forwarding to NTLM\n"); 64 ret = ntlm_AcquireCredentialsHandleW( pszPrincipal, ntlmW, fCredentialUse, 65 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, 66 phCredential, ptsExpiry ); 67 if (ret == SEC_E_OK) 68 { 69 NtlmCredentials *cred = (NtlmCredentials *)phCredential->dwLower; 70 cred->no_cached_credentials = (pAuthData == NULL); 71 } 72 return ret; 73 } 74 75 /*********************************************************************** 76 * AcquireCredentialsHandleA 77 */ 78 static SECURITY_STATUS SEC_ENTRY nego_AcquireCredentialsHandleA( 79 SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse, 80 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn, 81 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry ) 82 { 83 SECURITY_STATUS ret = SEC_E_INSUFFICIENT_MEMORY; 84 SEC_WCHAR *user = NULL, *domain = NULL, *passwd = NULL, *package = NULL; 85 SEC_WINNT_AUTH_IDENTITY_W *identityW = NULL; 86 87 TRACE("%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p\n", 88 debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse, 89 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry); 90 91 if (pszPackage) 92 { 93 int package_len = MultiByteToWideChar( CP_ACP, 0, pszPackage, -1, NULL, 0 ); 94 package = HeapAlloc( GetProcessHeap(), 0, package_len * sizeof(SEC_WCHAR) ); 95 if (!package) return SEC_E_INSUFFICIENT_MEMORY; 96 MultiByteToWideChar( CP_ACP, 0, pszPackage, -1, package, package_len ); 97 } 98 if (pAuthData) 99 { 100 SEC_WINNT_AUTH_IDENTITY_A *identity = pAuthData; 101 int user_len, domain_len, passwd_len; 102 103 if (identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI) 104 { 105 identityW = HeapAlloc( GetProcessHeap(), 0, sizeof(*identityW) ); 106 if (!identityW) goto done; 107 108 if (!identity->UserLength) user_len = 0; 109 else 110 { 111 user_len = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->User, 112 identity->UserLength, NULL, 0 ); 113 user = HeapAlloc( GetProcessHeap(), 0, user_len * sizeof(SEC_WCHAR) ); 114 if (!user) goto done; 115 MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->User, identity->UserLength, 116 user, user_len ); 117 } 118 if (!identity->DomainLength) domain_len = 0; 119 else 120 { 121 domain_len = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Domain, 122 identity->DomainLength, NULL, 0 ); 123 domain = HeapAlloc( GetProcessHeap(), 0, domain_len * sizeof(SEC_WCHAR) ); 124 if (!domain) goto done; 125 MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Domain, identity->DomainLength, 126 domain, domain_len ); 127 } 128 if (!identity->PasswordLength) passwd_len = 0; 129 else 130 { 131 passwd_len = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Password, 132 identity->PasswordLength, NULL, 0 ); 133 passwd = HeapAlloc( GetProcessHeap(), 0, passwd_len * sizeof(SEC_WCHAR) ); 134 if (!passwd) goto done; 135 MultiByteToWideChar( CP_ACP, 0, (LPCSTR)identity->Password, identity->PasswordLength, 136 passwd, passwd_len ); 137 } 138 identityW->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; 139 identityW->User = user; 140 identityW->UserLength = user_len; 141 identityW->Domain = domain; 142 identityW->DomainLength = domain_len; 143 identityW->Password = passwd; 144 identityW->PasswordLength = passwd_len; 145 } 146 else identityW = (SEC_WINNT_AUTH_IDENTITY_W *)identity; 147 } 148 ret = nego_AcquireCredentialsHandleW( NULL, package, fCredentialUse, pLogonID, identityW, 149 pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry ); 150 done: 151 HeapFree( GetProcessHeap(), 0, package ); 152 HeapFree( GetProcessHeap(), 0, user ); 153 HeapFree( GetProcessHeap(), 0, domain ); 154 HeapFree( GetProcessHeap(), 0, passwd ); 155 HeapFree( GetProcessHeap(), 0, identityW ); 156 return ret; 157 } 158 159 /*********************************************************************** 160 * InitializeSecurityContextW 161 */ 162 static SECURITY_STATUS SEC_ENTRY nego_InitializeSecurityContextW( 163 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName, 164 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 165 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, 166 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry ) 167 { 168 TRACE("%p, %p, %s, 0x%08x, %u, %u, %p, %u, %p, %p, %p, %p\n", 169 phCredential, phContext, debugstr_w(pszTargetName), fContextReq, 170 Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, 171 pfContextAttr, ptsExpiry); 172 173 return ntlm_InitializeSecurityContextW( phCredential, phContext, pszTargetName, 174 fContextReq, Reserved1, TargetDataRep, 175 pInput, Reserved2, phNewContext, 176 pOutput, pfContextAttr, ptsExpiry ); 177 } 178 179 /*********************************************************************** 180 * InitializeSecurityContextA 181 */ 182 static SECURITY_STATUS SEC_ENTRY nego_InitializeSecurityContextA( 183 PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName, 184 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, 185 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext, 186 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry ) 187 { 188 SECURITY_STATUS ret; 189 SEC_WCHAR *target = NULL; 190 191 TRACE("%p, %p, %s, 0x%08x, %u, %u, %p, %u, %p, %p, %p, %p\n", 192 phCredential, phContext, debugstr_a(pszTargetName), fContextReq, 193 Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, 194 pfContextAttr, ptsExpiry); 195 196 if (pszTargetName) 197 { 198 int target_len = MultiByteToWideChar( CP_ACP, 0, pszTargetName, -1, NULL, 0 ); 199 target = HeapAlloc(GetProcessHeap(), 0, target_len * sizeof(SEC_WCHAR) ); 200 if (!target) return SEC_E_INSUFFICIENT_MEMORY; 201 MultiByteToWideChar( CP_ACP, 0, pszTargetName, -1, target, target_len ); 202 } 203 ret = nego_InitializeSecurityContextW( phCredential, phContext, target, fContextReq, 204 Reserved1, TargetDataRep, pInput, Reserved2, 205 phNewContext, pOutput, pfContextAttr, ptsExpiry ); 206 HeapFree( GetProcessHeap(), 0, target ); 207 return ret; 208 } 209 210 /*********************************************************************** 211 * AcceptSecurityContext 212 */ 213 static SECURITY_STATUS SEC_ENTRY nego_AcceptSecurityContext( 214 PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput, 215 ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext, 216 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry) 217 { 218 TRACE("%p, %p, %p, 0x%08x, %u, %p, %p, %p, %p\n", phCredential, phContext, 219 pInput, fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr, 220 ptsExpiry); 221 222 return ntlm_AcceptSecurityContext( phCredential, phContext, pInput, 223 fContextReq, TargetDataRep, phNewContext, 224 pOutput, pfContextAttr, ptsExpiry ); 225 } 226 227 /*********************************************************************** 228 * CompleteAuthToken 229 */ 230 static SECURITY_STATUS SEC_ENTRY nego_CompleteAuthToken(PCtxtHandle phContext, 231 PSecBufferDesc pToken) 232 { 233 SECURITY_STATUS ret; 234 235 TRACE("%p %p\n", phContext, pToken); 236 if (phContext) 237 { 238 ret = SEC_E_UNSUPPORTED_FUNCTION; 239 } 240 else 241 { 242 ret = SEC_E_INVALID_HANDLE; 243 } 244 return ret; 245 } 246 247 /*********************************************************************** 248 * DeleteSecurityContext 249 */ 250 static SECURITY_STATUS SEC_ENTRY nego_DeleteSecurityContext(PCtxtHandle phContext) 251 { 252 TRACE("%p\n", phContext); 253 254 return ntlm_DeleteSecurityContext( phContext ); 255 } 256 257 /*********************************************************************** 258 * ApplyControlToken 259 */ 260 static SECURITY_STATUS SEC_ENTRY nego_ApplyControlToken(PCtxtHandle phContext, 261 PSecBufferDesc pInput) 262 { 263 SECURITY_STATUS ret; 264 265 TRACE("%p %p\n", phContext, pInput); 266 if (phContext) 267 { 268 ret = SEC_E_UNSUPPORTED_FUNCTION; 269 } 270 else 271 { 272 ret = SEC_E_INVALID_HANDLE; 273 } 274 return ret; 275 } 276 277 /*********************************************************************** 278 * QueryContextAttributesW 279 */ 280 static SECURITY_STATUS SEC_ENTRY nego_QueryContextAttributesW( 281 PCtxtHandle phContext, ULONG ulAttribute, void *pBuffer) 282 { 283 TRACE("%p, %u, %p\n", phContext, ulAttribute, pBuffer); 284 285 switch (ulAttribute) 286 { 287 case SECPKG_ATTR_SIZES: 288 { 289 SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)pBuffer; 290 sizes->cbMaxToken = 2888; 291 sizes->cbMaxSignature = 16; 292 sizes->cbSecurityTrailer = 16; 293 sizes->cbBlockSize = 0; 294 return SEC_E_OK; 295 } 296 case SECPKG_ATTR_NEGOTIATION_INFO: 297 { 298 SecPkgContext_NegotiationInfoW *info = (SecPkgContext_NegotiationInfoW *)pBuffer; 299 info->PackageInfo = ntlm_package_infoW; 300 info->NegotiationState = SECPKG_NEGOTIATION_COMPLETE; 301 return SEC_E_OK; 302 } 303 default: 304 return ntlm_QueryContextAttributesW( phContext, ulAttribute, pBuffer ); 305 } 306 } 307 308 /*********************************************************************** 309 * QueryContextAttributesA 310 */ 311 static SECURITY_STATUS SEC_ENTRY nego_QueryContextAttributesA(PCtxtHandle phContext, 312 ULONG ulAttribute, void *pBuffer) 313 { 314 TRACE("%p, %u, %p\n", phContext, ulAttribute, pBuffer); 315 316 switch (ulAttribute) 317 { 318 case SECPKG_ATTR_SIZES: 319 { 320 SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)pBuffer; 321 sizes->cbMaxToken = 2888; 322 sizes->cbMaxSignature = 16; 323 sizes->cbSecurityTrailer = 16; 324 sizes->cbBlockSize = 0; 325 return SEC_E_OK; 326 } 327 case SECPKG_ATTR_NEGOTIATION_INFO: 328 { 329 SecPkgContext_NegotiationInfoA *info = (SecPkgContext_NegotiationInfoA *)pBuffer; 330 info->PackageInfo = ntlm_package_infoA; 331 info->NegotiationState = SECPKG_NEGOTIATION_COMPLETE; 332 return SEC_E_OK; 333 } 334 default: 335 return ntlm_QueryContextAttributesA( phContext, ulAttribute, pBuffer ); 336 } 337 } 338 339 /*********************************************************************** 340 * ImpersonateSecurityContext 341 */ 342 static SECURITY_STATUS SEC_ENTRY nego_ImpersonateSecurityContext(PCtxtHandle phContext) 343 { 344 SECURITY_STATUS ret; 345 346 TRACE("%p\n", phContext); 347 if (phContext) 348 { 349 ret = SEC_E_UNSUPPORTED_FUNCTION; 350 } 351 else 352 { 353 ret = SEC_E_INVALID_HANDLE; 354 } 355 return ret; 356 } 357 358 /*********************************************************************** 359 * RevertSecurityContext 360 */ 361 static SECURITY_STATUS SEC_ENTRY nego_RevertSecurityContext(PCtxtHandle phContext) 362 { 363 SECURITY_STATUS ret; 364 365 TRACE("%p\n", phContext); 366 if (phContext) 367 { 368 ret = SEC_E_UNSUPPORTED_FUNCTION; 369 } 370 else 371 { 372 ret = SEC_E_INVALID_HANDLE; 373 } 374 return ret; 375 } 376 377 /*********************************************************************** 378 * MakeSignature 379 */ 380 static SECURITY_STATUS SEC_ENTRY nego_MakeSignature(PCtxtHandle phContext, 381 ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo) 382 { 383 TRACE("%p, 0x%08x, %p, %u\n", phContext, fQOP, pMessage, MessageSeqNo); 384 385 return ntlm_MakeSignature( phContext, fQOP, pMessage, MessageSeqNo ); 386 } 387 388 /*********************************************************************** 389 * VerifySignature 390 */ 391 static SECURITY_STATUS SEC_ENTRY nego_VerifySignature(PCtxtHandle phContext, 392 PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP) 393 { 394 TRACE("%p, %p, %u, %p\n", phContext, pMessage, MessageSeqNo, pfQOP); 395 396 return ntlm_VerifySignature( phContext, pMessage, MessageSeqNo, pfQOP ); 397 } 398 399 /*********************************************************************** 400 * FreeCredentialsHandle 401 */ 402 static SECURITY_STATUS SEC_ENTRY nego_FreeCredentialsHandle(PCredHandle phCredential) 403 { 404 TRACE("%p\n", phCredential); 405 406 return ntlm_FreeCredentialsHandle( phCredential ); 407 } 408 409 /*********************************************************************** 410 * EncryptMessage 411 */ 412 static SECURITY_STATUS SEC_ENTRY nego_EncryptMessage(PCtxtHandle phContext, 413 ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo) 414 { 415 TRACE("%p, 0x%08x, %p, %u\n", phContext, fQOP, pMessage, MessageSeqNo); 416 417 return ntlm_EncryptMessage( phContext, fQOP, pMessage, MessageSeqNo ); 418 } 419 420 /*********************************************************************** 421 * DecryptMessage 422 */ 423 static SECURITY_STATUS SEC_ENTRY nego_DecryptMessage(PCtxtHandle phContext, 424 PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP) 425 { 426 TRACE("%p, %p, %u, %p\n", phContext, pMessage, MessageSeqNo, pfQOP); 427 428 return ntlm_DecryptMessage( phContext, pMessage, MessageSeqNo, pfQOP ); 429 } 430 431 static const SecurityFunctionTableA negoTableA = { 432 1, 433 NULL, /* EnumerateSecurityPackagesA */ 434 nego_QueryCredentialsAttributesA, /* QueryCredentialsAttributesA */ 435 nego_AcquireCredentialsHandleA, /* AcquireCredentialsHandleA */ 436 nego_FreeCredentialsHandle, /* FreeCredentialsHandle */ 437 NULL, /* Reserved2 */ 438 nego_InitializeSecurityContextA, /* InitializeSecurityContextA */ 439 nego_AcceptSecurityContext, /* AcceptSecurityContext */ 440 nego_CompleteAuthToken, /* CompleteAuthToken */ 441 nego_DeleteSecurityContext, /* DeleteSecurityContext */ 442 nego_ApplyControlToken, /* ApplyControlToken */ 443 nego_QueryContextAttributesA, /* QueryContextAttributesA */ 444 nego_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ 445 nego_RevertSecurityContext, /* RevertSecurityContext */ 446 nego_MakeSignature, /* MakeSignature */ 447 nego_VerifySignature, /* VerifySignature */ 448 FreeContextBuffer, /* FreeContextBuffer */ 449 NULL, /* QuerySecurityPackageInfoA */ 450 NULL, /* Reserved3 */ 451 NULL, /* Reserved4 */ 452 NULL, /* ExportSecurityContext */ 453 NULL, /* ImportSecurityContextA */ 454 NULL, /* AddCredentialsA */ 455 NULL, /* Reserved8 */ 456 NULL, /* QuerySecurityContextToken */ 457 nego_EncryptMessage, /* EncryptMessage */ 458 nego_DecryptMessage, /* DecryptMessage */ 459 NULL, /* SetContextAttributesA */ 460 }; 461 462 static const SecurityFunctionTableW negoTableW = { 463 1, 464 NULL, /* EnumerateSecurityPackagesW */ 465 nego_QueryCredentialsAttributesW, /* QueryCredentialsAttributesW */ 466 nego_AcquireCredentialsHandleW, /* AcquireCredentialsHandleW */ 467 nego_FreeCredentialsHandle, /* FreeCredentialsHandle */ 468 NULL, /* Reserved2 */ 469 nego_InitializeSecurityContextW, /* InitializeSecurityContextW */ 470 nego_AcceptSecurityContext, /* AcceptSecurityContext */ 471 nego_CompleteAuthToken, /* CompleteAuthToken */ 472 nego_DeleteSecurityContext, /* DeleteSecurityContext */ 473 nego_ApplyControlToken, /* ApplyControlToken */ 474 nego_QueryContextAttributesW, /* QueryContextAttributesW */ 475 nego_ImpersonateSecurityContext, /* ImpersonateSecurityContext */ 476 nego_RevertSecurityContext, /* RevertSecurityContext */ 477 nego_MakeSignature, /* MakeSignature */ 478 nego_VerifySignature, /* VerifySignature */ 479 FreeContextBuffer, /* FreeContextBuffer */ 480 NULL, /* QuerySecurityPackageInfoW */ 481 NULL, /* Reserved3 */ 482 NULL, /* Reserved4 */ 483 NULL, /* ExportSecurityContext */ 484 NULL, /* ImportSecurityContextW */ 485 NULL, /* AddCredentialsW */ 486 NULL, /* Reserved8 */ 487 NULL, /* QuerySecurityContextToken */ 488 nego_EncryptMessage, /* EncryptMessage */ 489 nego_DecryptMessage, /* DecryptMessage */ 490 NULL, /* SetContextAttributesW */ 491 }; 492 493 #define NEGO_MAX_TOKEN 12000 494 495 static WCHAR nego_name_W[] = {'N','e','g','o','t','i','a','t','e',0}; 496 static char nego_name_A[] = "Negotiate"; 497 498 static WCHAR negotiate_comment_W[] = 499 {'M','i','c','r','o','s','o','f','t',' ','P','a','c','k','a','g','e',' ', 500 'N','e','g','o','t','i','a','t','o','r',0}; 501 static CHAR negotiate_comment_A[] = "Microsoft Package Negotiator"; 502 503 #define CAPS ( \ 504 SECPKG_FLAG_INTEGRITY | \ 505 SECPKG_FLAG_PRIVACY | \ 506 SECPKG_FLAG_CONNECTION | \ 507 SECPKG_FLAG_MULTI_REQUIRED | \ 508 SECPKG_FLAG_EXTENDED_ERROR | \ 509 SECPKG_FLAG_IMPERSONATION | \ 510 SECPKG_FLAG_ACCEPT_WIN32_NAME | \ 511 SECPKG_FLAG_NEGOTIABLE | \ 512 SECPKG_FLAG_GSS_COMPATIBLE | \ 513 SECPKG_FLAG_LOGON | \ 514 SECPKG_FLAG_RESTRICTED_TOKENS ) 515 516 void SECUR32_initNegotiateSP(void) 517 { 518 SecureProvider *provider = SECUR32_addProvider(&negoTableA, &negoTableW, NULL); 519 520 const SecPkgInfoW infoW = {CAPS, 1, RPC_C_AUTHN_GSS_NEGOTIATE, NEGO_MAX_TOKEN, 521 nego_name_W, negotiate_comment_W}; 522 const SecPkgInfoA infoA = {CAPS, 1, RPC_C_AUTHN_GSS_NEGOTIATE, NEGO_MAX_TOKEN, 523 nego_name_A, negotiate_comment_A}; 524 SECUR32_addPackages(provider, 1L, &infoA, &infoW); 525 } 526