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
36extern "C" {
37#include "xpt-b64.h"
38#include "stdio.h"
39}
40
41#include "xpl_Logger.h"
42
43extern int g_cancelSession;
44
45/*==================================================================================================
46                                 SOURCE FUNCTIONS
47==================================================================================================*/
48
49
50/*==================================================================================================
51FUNCTION        : DMServerSession::DMServerSession
52
53DESCRIPTION     : The class constructor.
54ARGUMENT PASSED : sml_ContentType
55                  sml_EncodingType
56OUTPUT PARAMETER:
57RETURN VALUE    :
58IMPORTANT NOTES :
59
60==================================================================================================*/
61DMServerSession::DMServerSession()
62{
63
64    m_nSecState = DM_CLIENT_NO_SERVER_NO_AUTH;
65}
66
67
68
69
70/*==================================================================================================
71FUNCTION        : DMServerSession::ConnectServer
72
73DESCRIPTION     : 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.
78ARGUMENT PASSED : server_Id
79OUTPUT PARAMETER:
80RETURN VALUE    :
81IMPORTANT NOTES :
82
83==================================================================================================*/
84SYNCML_DM_RET_STATUS_T
85DMServerSession::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/*==================================================================================================
157FUNCTION        : DMServerSession::BuildSendPackageOne
158
159DESCRIPTION     : 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.
165ARGUMENT PASSED : session_Direction
166                  p_ParsedPk0
167OUTPUT PARAMETER:
168RETURN VALUE    : STYNCML_DM_SUCCES for success or SYNCML_DM_FAIL for failure
169IMPORTANT NOTES :
170
171
172==================================================================================================*/
173SYNCML_DM_RET_STATUS_T
174DMServerSession::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/*==================================================================================================
208FUNCTION        : DMServerSession::SessionStart
209
210DESCRIPTION     : 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.
216ARGUMENT PASSED : p_SessionStart
217OUTPUT PARAMETER:
218RETURN VALUE    :
219IMPORTANT NOTES :
220
221
222==================================================================================================*/
223SYNCML_DM_RET_STATUS_T
224DMServerSession::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/*==================================================================================================
335FUNCTION        : DMServerSession::SetURI
336
337DESCRIPTION     : 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.
341ARGUMENT PASSED : p_RespURI
342OUTPUT PARAMETER:
343RETURN VALUE    : SYNCML_DM_SUCCESS if success,
344                  SYNCML_DM_FAIL if failed.
345IMPORTANT NOTES :
346
347
348==================================================================================================*/
349SYNCML_DM_RET_STATUS_T
350DMServerSession::SetURI (const char *p_RespURI)
351{
352    return m_oConnObject.SetURI(p_RespURI);
353}
354
355
356
357/*==================================================================================================
358FUNCTION        : DMServerSession::RecvPackage
359
360DESCRIPTION     : 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.
363ARGUMENT PASSED :
364OUTPUT PARAMETER:
365RETURN VALUE    :
366IMPORTANT NOTES :
367
368
369==================================================================================================*/
370SYNCML_DM_RET_STATUS_T
371DMServerSession::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/*==================================================================================================
493FUNCTION        : DMServerSession::SendPackage
494
495DESCRIPTION     : 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.
498ARGUMENT PASSED :
499OUTPUT PARAMETER:
500RETURN VALUE    :
501IMPORTANT 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==================================================================================================*/
506SYNCML_DM_RET_STATUS_T
507DMServerSession::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/*==================================================================================================
598FUNCTION        : DMProcessScriptSession::SetServCredsMissing
599
600DESCRIPTION     : This function reset class data members value to defaults after one DM session is
601                  ended.
602ARGUMENT PASSED :
603OUTPUT PARAMETER:
604RETURN VALUE    :
605IMPORTANT NOTES :
606
607==================================================================================================*/
608void
609DMServerSession::SetServCredsMissing( BOOLEAN newIsServCredsMissing )
610{
611   isServCredsMissing = newIsServCredsMissing;
612}
613