1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /*================================================================================================== 18 19 Source Name: DMServerSession.cc 20 21 General Description: Implementation of DMServerSession class. 22 23 ==================================================================================================*/ 24 25 #include "dmServerSession.h" 26 #include "dm_ua_handlecommand.h" 27 #include "dm_tree_util.h" 28 #include "xpl_dm_Manager.h" 29 #include "xpl_dm_Notifications.h" 30 #include "dmLockingHelper.h" 31 32 #ifndef DM_NO_LOCKING 33 #include "dmThreadQueue.h" 34 #endif 35 36 extern "C" { 37 #include "xpt-b64.h" 38 #include "stdio.h" 39 } 40 41 #include "xpl_Logger.h" 42 43 extern int g_cancelSession; 44 45 /*================================================================================================== 46 SOURCE FUNCTIONS 47 ==================================================================================================*/ 48 49 50 /*================================================================================================== 51 FUNCTION : DMServerSession::DMServerSession 52 53 DESCRIPTION : The class constructor. 54 ARGUMENT PASSED : sml_ContentType 55 sml_EncodingType 56 OUTPUT PARAMETER: 57 RETURN VALUE : 58 IMPORTANT NOTES : 59 60 ==================================================================================================*/ 61 DMServerSession::DMServerSession() 62 { 63 64 m_nSecState = DM_CLIENT_NO_SERVER_NO_AUTH; 65 } 66 67 68 69 70 /*================================================================================================== 71 FUNCTION : DMServerSession::ConnectServer 72 73 DESCRIPTION : The SessionStart() function calls this function to connect to the server. 74 The function will perform the following operations: 75 1) Get the DM account URI from the DM tree. 76 2) Call SYNCML_DM_TransportController::Connect() function to establish the DM 77 session clent/server connection. 78 ARGUMENT PASSED : server_Id 79 OUTPUT PARAMETER: 80 RETURN VALUE : 81 IMPORTANT NOTES : 82 83 ==================================================================================================*/ 84 SYNCML_DM_RET_STATUS_T 85 DMServerSession::ConnectServer(CPCHAR pServerId) 86 { 87 SYNCML_DM_RET_STATUS_T ret_stat = SYNCML_DM_SUCCESS; 88 XPL_ADDR_TYPE_T addressType = XPL_ADDR_DEFAULT; 89 CPCHAR szDMAccRootPath = ::XPL_DM_GetEnv( SYNCML_DM_DMACC_ROOT_PATH ); 90 CPCHAR szServerIdNodeName = ::XPL_DM_GetEnv( SYNCML_DM_NODENAME_SERVERID ); 91 92 93 XPL_LOG_DM_SESS_Debug(("Entered DMServerSession::ConnectServer\n")); 94 95 /* Get the current server ID's DM account URI */ 96 if ( !dmTreeObj.GetParentOfKeyValue (pServerId, 97 szServerIdNodeName, 98 szDMAccRootPath, 99 clientServerCreds.pDMAccNodeName) ) 100 { 101 XPL_LOG_DM_SESS_Error(("Cannot Get Parent for %s\n",szServerIdNodeName )); 102 return SYNCML_DM_FAIL; 103 } 104 105 DMGetData oDataAddr, oDataAddrType, oDataPort, oData; 106 107 ret_stat = dmTreeObj.GetDefAccountAddrInfo((CPCHAR)clientServerCreds.pDMAccNodeName, 108 oDataAddr, 109 oDataAddrType, 110 oDataPort ); 111 112 if ( ret_stat != SYNCML_DM_SUCCESS ) 113 { 114 XPL_LOG_DM_SESS_Error(("Cannot Get Account Address Info for %s\n",(CPCHAR)clientServerCreds.pDMAccNodeName )); 115 return ret_stat; 116 } 117 118 if ( dmTreeObj.IsVersion_12() ) 119 { 120 // Get the value of the pbDMAccUri/PrefConRef node 121 addressType = XPL_ADDR_HTTP; 122 ret_stat = dmTreeObj.GetAccNodeValue((CPCHAR)clientServerCreds.pDMAccNodeName, 123 DM_PREFCONREF, 124 oData); 125 } 126 else 127 { 128 if ( oDataAddrType.getCharData() ) 129 addressType = DmAtoi(oDataAddrType.getCharData()); 130 else 131 addressType = XPL_ADDR_HTTP; 132 133 if ( addressType != XPL_ADDR_HTTP && addressType != XPL_ADDR_WSP ) 134 return SYNCML_DM_FAIL; 135 136 ret_stat = dmTreeObj.GetAccNodeValue((CPCHAR)clientServerCreds.pDMAccNodeName, 137 DM_CONREF, 138 oData); 139 } 140 141 if ( ret_stat == SYNCML_DM_SUCCESS ) 142 { dmTreeObj.SetConRef(oData.getCharData()); 143 ret_stat = m_oConnObject.Init(g_iDMWorkspaceSize,addressType,oData.getCharData()); 144 } 145 else 146 if ( ret_stat == SYNCML_DM_NOT_FOUND ) 147 { dmTreeObj.SetConRef(NULL); 148 ret_stat = m_oConnObject.Init(g_iDMWorkspaceSize,addressType,NULL); 149 } 150 151 XPL_LOG_DM_SESS_Debug (("Leaving DMServerSession::ConnectServer ret_stat=%d\n",ret_stat)); 152 return (ret_stat); 153 } 154 155 156 /*================================================================================================== 157 FUNCTION : DMServerSession::BuildSendPackageOne 158 159 DESCRIPTION : SessionStart function calls this function to build the DM package one and send it 160 to the server. 161 The function will perform the following operations: 162 1) Create the SYNCML_DM_BuildPackage object. 163 2) Build the package one document. 164 3) Send the package one to the Transport Binding. 165 ARGUMENT PASSED : session_Direction 166 p_ParsedPk0 167 OUTPUT PARAMETER: 168 RETURN VALUE : STYNCML_DM_SUCCES for success or SYNCML_DM_FAIL for failure 169 IMPORTANT NOTES : 170 171 172 ==================================================================================================*/ 173 SYNCML_DM_RET_STATUS_T 174 DMServerSession::BuildSendPackageOne(CPCHAR pServerID, DmtSessionProp * pSessionProp) 175 { 176 XPL_LOG_DM_SESS_Debug (("Entering DMServerSession::BuildSendPackageOne serverId = %s\n", pServerID)); 177 SYNCML_DM_RET_STATUS_T ret_stat = SYNCML_DM_SUCCESS; 178 m_oPkgBuilder.Init(this); 179 180 #ifndef DM_NO_LOCKING 181 INT32 nLockID = 0; 182 { 183 DMLockingHelper oLock( 0, ".", pServerID,SYNCML_DM_LOCK_TYPE_EXCLUSIVE, FALSE); 184 nLockID = oLock.GetID(); 185 186 if ( !oLock.IsLockedSuccessfully() ) 187 { 188 return SYNCML_DM_FAIL; 189 } 190 191 ret_stat = m_oPkgBuilder.BuildPackageOne(pServerID, pSessionProp); 192 } 193 194 dmTreeObj.ReleaseLock( nLockID ); 195 #else 196 ret_stat = m_oPkgBuilder.BuildPackageOne(pServerID, pSessionProp); 197 #endif 198 if ( ret_stat == SYNCML_DM_SUCCESS ) 199 { 200 ret_stat = SendPackage(); 201 } 202 203 return (ret_stat); 204 } 205 206 207 /*================================================================================================== 208 FUNCTION : DMServerSession::SessionStart 209 210 DESCRIPTION : The UserAgen::SessionStart calls this function after it creates MgmtSession object. 211 The function will perform the following operations: 212 1) Call SessionStart() to setup the DM tree. 213 2) Register the DM engine with the SYNCML toolkit. 214 3) Connect the client with the server. 215 4) Build and send the package one. 216 ARGUMENT PASSED : p_SessionStart 217 OUTPUT PARAMETER: 218 RETURN VALUE : 219 IMPORTANT NOTES : 220 221 222 ==================================================================================================*/ 223 SYNCML_DM_RET_STATUS_T 224 DMServerSession::Start(CPCHAR pServerID, 225 DmtSessionProp * pSessionProp) 226 { 227 SYNCML_DM_RET_STATUS_T ret_stat; 228 BOOLEAN bMoreMessage = TRUE; 229 230 ret_stat = Init(pSessionProp->isWBXML()); 231 if ( ret_stat != SYNCML_DM_SUCCESS ) 232 return ret_stat; 233 234 /* Register the DM engine with the SYNCML toolkit. */ 235 userData.pSessionMng = this; 236 userData.pPkgBuilder = &m_oPkgBuilder; 237 ret_stat = RegisterDmEngineWithSyncmlToolkit(&userData); 238 if ( ret_stat != SYNCML_DM_SUCCESS ) 239 { 240 XPL_LOG_DM_SESS_Debug(("Exiting: RegisterDmEngineWithSyncmlToolkit failed\n")); 241 return (ret_stat); 242 } 243 244 /* Create connection between server and client */ 245 ret_stat = ConnectServer(pServerID); 246 if (ret_stat != SYNCML_DM_SUCCESS) 247 { 248 XPL_LOG_DM_SESS_Error(("Cannot connect server\n")); 249 return (ret_stat); 250 } 251 252 /* Remember the sessionDirection in case we have to resend the Alert.*/ 253 pSessionProp->generateSessionID(); 254 serverSessionId = pSessionProp->getSessionID(); 255 256 257 /* Build and send the package one to the Server. */ 258 ret_stat = BuildSendPackageOne(pServerID, pSessionProp); 259 if (ret_stat != SYNCML_DM_SUCCESS) 260 return (ret_stat); 261 262 XPL_DM_NotifySessionProgress(TRUE); 263 264 while (bMoreMessage == TRUE && g_cancelSession == 0) 265 { 266 /* Call the method to receive the package from the transport.*/ 267 ret_stat = RecvPackage(); 268 if (ret_stat != SYNCML_DM_SUCCESS) 269 { 270 XPL_DM_NotifySessionProgress(FALSE); 271 return ret_stat; 272 } 273 274 /* Call the method to parse and handle the commands in the package.*/ 275 commandCount = 0; 276 277 #ifndef DM_NO_LOCKING 278 int nLockID = 0; 279 280 { 281 DMLockingHelper oLock( 0, ".", pServerID, SYNCML_DM_LOCK_TYPE_EXCLUSIVE, FALSE ); 282 nLockID = oLock.GetID(); 283 284 if ( !oLock.IsLockedSuccessfully() ) 285 { 286 XPL_DM_NotifySessionProgress(FALSE); 287 return SYNCML_DM_FAIL; 288 } 289 290 ret_stat = ParseMessage(); 291 } 292 293 dmTreeObj.ReleaseLock( nLockID ); 294 #else 295 ret_stat = ParseMessage(); 296 #endif 297 298 if (ret_stat != SYNCML_DM_SUCCESS) 299 { 300 XPL_DM_NotifySessionProgress(FALSE); 301 return ret_stat; 302 } 303 304 /* Check to see if we are passed the max retries */ 305 if (serverRetryCount > MAX_AUTH_RETRY || clientRetryCount > MAX_AUTH_RETRY) 306 { 307 XPL_DM_NotifySessionProgress(FALSE); 308 return SYNCML_DM_SESSION_AUTH_FAIL; 309 } 310 311 /* Check to see if any operational commands were parsed.*/ 312 if ( commandCount != 0 && m_bSessionAborted == FALSE && g_cancelSession == 0) 313 { 314 ret_stat = SendPackage(); 315 if (ret_stat != SYNCML_DM_SUCCESS) 316 { 317 XPL_DM_NotifySessionProgress(FALSE); 318 return ret_stat; 319 } 320 } 321 else 322 { 323 /* End the DM Session.*/ 324 bMoreMessage = FALSE; 325 326 } 327 } 328 329 XPL_DM_NotifySessionProgress(FALSE); 330 return SYNCML_DM_SUCCESS; 331 } 332 333 334 /*================================================================================================== 335 FUNCTION : DMServerSession::SetURI 336 337 DESCRIPTION : The UserAgent::SetURI will call this function if receiving package set the 338 response URI to a new value. 339 The function will call Connection object to set the URI of the SyncML server 340 for HTTP/WSP. 341 ARGUMENT PASSED : p_RespURI 342 OUTPUT PARAMETER: 343 RETURN VALUE : SYNCML_DM_SUCCESS if success, 344 SYNCML_DM_FAIL if failed. 345 IMPORTANT NOTES : 346 347 348 ==================================================================================================*/ 349 SYNCML_DM_RET_STATUS_T 350 DMServerSession::SetURI (const char *p_RespURI) 351 { 352 return m_oConnObject.SetURI(p_RespURI); 353 } 354 355 356 357 /*================================================================================================== 358 FUNCTION : DMServerSession::RecvPackage 359 360 DESCRIPTION : The UserAgent::TransportMsg will call this function when data is received 361 This function calls SYNCML_DM_Connection::Recv() to get the DM docuemnt from the 362 Transport. 363 ARGUMENT PASSED : 364 OUTPUT PARAMETER: 365 RETURN VALUE : 366 IMPORTANT NOTES : 367 368 369 ==================================================================================================*/ 370 SYNCML_DM_RET_STATUS_T 371 DMServerSession::RecvPackage() 372 { 373 SYNCML_DM_RET_STATUS_T serverAuthStatus = SYNCML_DM_SUCCESS; 374 SYNCMLDM_SEC_CREDENTIALS_T *pGenHmac; 375 UINT8 decodedNonce[MAX_BIN_NONCE_LEN]; 376 UINT32 encodedNonceLen =0; 377 UINT32 decodedNonceLen =0; 378 SYNCML_DM_RET_STATUS_T retStat = SYNCML_DM_SUCCESS; 379 380 381 pWritePos = recvSmlDoc.pData; 382 383 /* Unlock the workspace so the Toolkit controls it again. */ 384 smlUnlockReadBuffer(sendInstanceId, workspaceUsedSize); 385 386 /* Check if the HMAC-MD5 headers were included.*/ 387 if( m_oRecvCredHeaders.empty() == FALSE ) 388 { 389 //XPL_LOG_DM_SESS_Debug(("m_oRecvCredHeaders is not empty.\n")); 390 //XPL_LOG_DM_SESS_Debug(("m_oRecvCredHeaders: username:%s\n", (CPCHAR)(m_oRecvCredHeaders.m_oUserName.getBuffer()))); 391 //XPL_LOG_DM_SESS_Debug(("m_oRecvCredHeaders: mac:%s\n", (CPCHAR)(m_oRecvCredHeaders.m_oMac.getBuffer()))); 392 393 SYNCMLDM_HMAC_SEC_INFO_T hmacSecInfo; 394 // since server sends hmac, use hmac after that 395 clientServerCreds.SetPrefServerAuth(SYNCML_DM_CHAL_HMAC); 396 397 /* Remember that the Cred Headers are here.*/ 398 SetServCredsMissing(FALSE); 399 400 /* Copy the received DM document into the SyncML workspace */ 401 /* Create the Server Credentials.*/ 402 hmacSecInfo.pb_user_name_or_server_id = (UINT8*)clientServerCreds.pServerId.c_str(); 403 hmacSecInfo.pb_password = (UINT8*)clientServerCreds.pServerPW.c_str(); 404 hmacSecInfo.o_encode_base64 = TRUE; /* Always true for HMAC */ 405 hmacSecInfo.pb_syncml_document = pWritePos; /* Pointer for SyncML Doc */ 406 hmacSecInfo.dw_syncml_document_length = recvSmlDoc.dataSize; /* Size of Doc*/ 407 408 /* Validate the HMAC-MD5 header from the HTTP header. 409 * We will check every message regardless of the Security state. 410 * The ServerNonce string is b64 encoded and must be decoded now.*/ 411 if(clientServerCreds.pServerNonce != NULL) 412 { 413 encodedNonceLen = DmStrlen((const char *)clientServerCreds.pServerNonce); 414 decodedNonceLen = base64Decode((unsigned char *)decodedNonce, 415 MAX_BIN_NONCE_LEN, 416 (unsigned char*)clientServerCreds.pServerNonce.c_str(), 417 (unsigned long*)&encodedNonceLen); 418 } 419 420 /* Generate the Server Credentials.*/ 421 hmacSecInfo.pb_nonce = decodedNonce; 422 hmacSecInfo.w_nonce_length = decodedNonceLen; 423 424 pGenHmac = syncmldm_sec_build_hmac_cred((const SYNCMLDM_HMAC_SEC_INFO_T *)&hmacSecInfo); 425 426 /* Compare the created creds to the received creds.*/ 427 if ( pGenHmac == NULL || !m_oRecvCredHeaders.m_oMac.compare((CPCHAR)pGenHmac->ab_credential_string, 428 pGenHmac->w_credential_string_length) ) 429 430 { 431 serverAuthStatus = SYNCML_DM_UNAUTHORIZED; 432 serverRetryCount++; 433 } 434 else 435 { 436 /* Reset count since server is now authenticated.*/ 437 serverRetryCount = 0; 438 } 439 DmFreeMem(pGenHmac); 440 441 /* Update the security state with the server authentication status. Note that 442 * we only check for cases that cause a change in the SecurityState. 443 */ 444 switch (m_nSecState) 445 { 446 case DM_CLIENT_NO_SERVER_NO_AUTH: 447 if (serverAuthStatus == SYNCML_DM_SUCCESS) 448 m_nSecState = DM_CLIENT_NO_SERVER_Y_AUTH; 449 break; 450 451 case DM_CLIENT_Y_SERVER_NO_AUTH: 452 if (serverAuthStatus == SYNCML_DM_SUCCESS) 453 m_nSecState = DM_BOTH_CLIENT_SERVER_AUTH; 454 break; 455 456 case DM_CLIENT_NO_SERVER_Y_AUTH: 457 if (serverAuthStatus != SYNCML_DM_SUCCESS) 458 m_nSecState = DM_CLIENT_NO_SERVER_NO_AUTH; 459 break; 460 461 case DM_BOTH_CLIENT_SERVER_AUTH: 462 if (serverAuthStatus != SYNCML_DM_SUCCESS) 463 m_nSecState = DM_CLIENT_Y_SERVER_NO_AUTH; 464 break; 465 466 default: 467 /* The Security State is messed up, so reset it.*/ 468 m_nSecState = DM_CLIENT_NO_SERVER_NO_AUTH; 469 break; 470 } 471 /* Update the User Agent's security state.*/ 472 473 } /* p_cred_headers != NULL */ 474 else 475 { 476 /* The HMAC-MD5 creditials are missing.*/ 477 if ( !IsServerAuthorized() ) 478 { 479 SetServCredsMissing(TRUE); 480 serverRetryCount++; 481 } 482 else 483 serverRetryCount = 0; 484 485 } 486 487 return(retStat); 488 489 } 490 491 492 /*================================================================================================== 493 FUNCTION : DMServerSession::SendPackage 494 495 DESCRIPTION : This function will be called when a DM package is built up and ready to send. 496 The function will call SYNCML_DM_Connection::Send() to send the DM document to 497 the remote server. 498 ARGUMENT PASSED : 499 OUTPUT PARAMETER: 500 RETURN VALUE : 501 IMPORTANT NOTES : Both sendSmlDoc and pRecvSmlDoc must pass to pConnObject->Send. pConnObject will 502 write receiving DM document to the pRecvSmlDoc after it sends sendSmlDoc. 503 504 505 ==================================================================================================*/ 506 SYNCML_DM_RET_STATUS_T 507 DMServerSession::SendPackage() 508 { 509 SYNCMLDM_HMAC_SEC_INFO_T hmacSecInfo; 510 SYNCMLDM_SEC_CREDENTIALS_T *pHmacCreds = NULL; 511 SYNCML_DM_RET_STATUS_T ret_stat; 512 UINT8 decodedNonce[MAX_BIN_NONCE_LEN]; 513 UINT32 encodedNonceLen = 0; 514 UINT32 decodedNonceLen = 0; 515 Ret_t sml_ret_stat; 516 517 /* Lock the workspace for reading and writing SyncML document. 518 * These buffers will be unlocked after the UA receives SyncML document */ 519 smlLockReadBuffer(sendInstanceId, &pReadPos, &workspaceUsedSize); 520 521 /* Set sendSmlDoc point to workspace */ 522 sendSmlDoc.dataSize = workspaceUsedSize; 523 sendSmlDoc.pData = pReadPos; 524 525 /* The ClientNonce string is b64 encoded and must be decoded now.*/ 526 if(clientServerCreds.pClientNonce != NULL) 527 { 528 const char *clientNonce = clientServerCreds.pClientNonce.c_str(); 529 encodedNonceLen = DmStrlen(clientNonce); 530 if (encodedNonceLen == 0) { 531 clientNonce = SERVER_RESYNC_NONCE; 532 encodedNonceLen = DmStrlen(clientNonce); 533 } 534 decodedNonceLen = base64Decode((UINT8*)decodedNonce, 535 MAX_BIN_NONCE_LEN, 536 (unsigned char*)clientNonce, 537 (unsigned long*)&encodedNonceLen); 538 } 539 /* Let's make up our credentials before we send the package. */ 540 hmacSecInfo.pb_user_name_or_server_id = (UINT8*)clientServerCreds.pClientUserName.c_str(); 541 hmacSecInfo.pb_password = (UINT8*)clientServerCreds.pClientPW.c_str(); 542 hmacSecInfo.pb_nonce = decodedNonce; 543 hmacSecInfo.pb_syncml_document = pReadPos; /* Used as the pointer to the SyncML Doc */ 544 hmacSecInfo.o_encode_base64 = TRUE; /* Always true for HMAC credentials */ 545 hmacSecInfo.w_nonce_length = decodedNonceLen; 546 hmacSecInfo.dw_syncml_document_length = workspaceUsedSize; /* Size of the SyncML Doc */ 547 548 if( clientServerCreds.AuthPrefCredType == SYNCML_DM_CHAL_HMAC ) 549 pHmacCreds = syncmldm_sec_build_hmac_cred((const SYNCMLDM_HMAC_SEC_INFO_T *)&hmacSecInfo); 550 551 m_oRecvCredHeaders.clear(); 552 if ( pHmacCreds != NULL ) 553 { 554 if ( pHmacCreds->w_credential_string_length ) 555 { 556 m_oRecvCredHeaders.m_oMac.assign(pHmacCreds->ab_credential_string,pHmacCreds->w_credential_string_length); 557 if ( m_oRecvCredHeaders.m_oMac.getBuffer() == NULL ) 558 { 559 FreeAndSetNull(pHmacCreds); 560 return SYNCML_DM_DEVICE_FULL; 561 } 562 } 563 564 m_oRecvCredHeaders.m_oAlgorithm.assign(SYNCML_MAC_ALG); 565 if ( m_oRecvCredHeaders.m_oAlgorithm.getBuffer() == NULL ) 566 { 567 FreeAndSetNull(pHmacCreds); 568 return SYNCML_DM_DEVICE_FULL; 569 } 570 571 if ( clientServerCreds.pClientUserName ) 572 { 573 m_oRecvCredHeaders.m_oUserName.assign((CPCHAR)clientServerCreds.pClientUserName); 574 if ( m_oRecvCredHeaders.m_oUserName.getBuffer() == NULL ) 575 { 576 FreeAndSetNull(pHmacCreds); 577 return SYNCML_DM_DEVICE_FULL; 578 } 579 } 580 } 581 582 sml_ret_stat = smlLockWriteBuffer(recvInstanceId, &pWritePos, &workspaceFreeSize); 583 584 if ( sml_ret_stat == SML_ERR_OK ) 585 { 586 recvSmlDoc.pData = pWritePos; 587 ret_stat = m_oConnObject.Send(&sendSmlDoc, &recvSmlDoc, smlContentType, &m_oRecvCredHeaders); 588 } 589 else 590 ret_stat = SYNCML_DM_FAIL; 591 592 FreeAndSetNull(pHmacCreds); 593 return (ret_stat); 594 } 595 596 597 /*================================================================================================== 598 FUNCTION : DMProcessScriptSession::SetServCredsMissing 599 600 DESCRIPTION : This function reset class data members value to defaults after one DM session is 601 ended. 602 ARGUMENT PASSED : 603 OUTPUT PARAMETER: 604 RETURN VALUE : 605 IMPORTANT NOTES : 606 607 ==================================================================================================*/ 608 void 609 DMServerSession::SetServCredsMissing( BOOLEAN newIsServCredsMissing ) 610 { 611 isServCredsMissing = newIsServCredsMissing; 612 } 613