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: dmServerAuthentication.cc
20
21    General Description: Implementation of DMServerAuthentication class.
22
23==================================================================================================*/
24
25#include "dmt.hpp"
26#include "dmStringUtil.h"
27#include "dm_security.h"
28#include "dm_tree_util.h"
29#include "dmSessionDefs.h"
30#include "SYNCML_DM_BuildPackage.H"
31#include "xpl_dm_Manager.h"
32#include "dmServerAuthentication.h"
33
34extern "C" {
35#include "xpt-b64.h"
36#include "stdio.h"
37}
38
39void DMServerAuthentication::CheckCredentials( SYNCML_DM_AuthContext_T& AuthContext, const DMString& password, const DMBuffer& data, BOOLEAN bDecodeNonce )
40{
41  UINT8   decodedNonce[MAX_BIN_NONCE_LEN];
42
43  XPL_LOG_DM_SESS_Debug(("CheckCredentials Entered\n"));
44
45  memset(decodedNonce, 0, MAX_BIN_NONCE_LEN);
46
47  UINT32 encodedNonceLen = data.getSize();
48
49  UINT32 decodedNonceLen = base64Decode((unsigned char *)decodedNonce, MAX_BIN_NONCE_LEN,(unsigned char*)data.getBuffer(),(unsigned long*) &encodedNonceLen);
50
51  SYNCMLDM_HMAC_SEC_INFO_T    hmacSecInfo;
52
53  memset(&hmacSecInfo,0,sizeof(hmacSecInfo));
54
55  /* Call the security library to generate the credentials.*/
56
57  hmacSecInfo.pb_user_name_or_server_id = (UINT8*)AuthContext._pServerId;
58  hmacSecInfo.pb_password               = (UINT8*)password.c_str();
59
60  if( bDecodeNonce )
61  {
62     hmacSecInfo.pb_nonce               = decodedNonce;
63     hmacSecInfo.w_nonce_length         = decodedNonceLen;
64  }
65  else
66  {
67     hmacSecInfo.pb_nonce                = (unsigned char*)data.getBuffer();
68     hmacSecInfo.w_nonce_length          = (unsigned long)data.getSize();
69
70     if( hmacSecInfo.pb_nonce == NULL )
71     {
72         hmacSecInfo.pb_nonce = (UINT8*)"";
73         hmacSecInfo.w_nonce_length = 0;
74     }
75  }
76
77  hmacSecInfo.pb_syncml_document        = AuthContext._pTrigger; /* Pointer to the Trigger portion of the Pkg0.*/
78  hmacSecInfo.dw_syncml_document_length = AuthContext._triggerLen;
79  hmacSecInfo.o_encode_base64           = FALSE;  /* The MD5 digest is 16 bytes of binary, not b64.*/
80
81  SYNCMLDM_SEC_CREDENTIALS_T *pGenCred = syncmldm_sec_build_hmac_cred(&hmacSecInfo);
82
83
84  if( pGenCred != NULL )
85  {
86
87      /* Compare the newly generated Credentials to the ones passed by the server.*/
88     char sdigest[2*pGenCred->w_credential_string_length+1];
89     char cdigest[2*pGenCred->w_credential_string_length+1];
90
91     for (int i=0; i<pGenCred->w_credential_string_length; i++ ) {
92        sprintf(sdigest+2*i, "%02X", (char)AuthContext._md5Digest[i]);
93     }
94
95     for (int i=0; i<pGenCred->w_credential_string_length; i++ ) {
96        sprintf(cdigest+2*i, "%02X", (char)pGenCred->ab_credential_string[i]);
97     }
98
99     sdigest[2*pGenCred->w_credential_string_length] = '\0';
100     cdigest[2*pGenCred->w_credential_string_length] = '\0';
101
102     XPL_LOG_DM_SESS_Debug(("Server Digest: %s\n", sdigest));
103     XPL_LOG_DM_SESS_Debug(("Client Digest: %s\n", cdigest));
104
105     AuthContext._AuthFlag = (pGenCred && (0 == memcmp((CPCHAR)AuthContext._md5Digest,(CPCHAR)pGenCred->ab_credential_string,pGenCred->w_credential_string_length )));
106     if( !AuthContext._AuthFlag )
107     {
108        XPL_LOG_DM_SESS_Error(("CheckCredentials Failed\n"));
109     }
110
111     DmFreeMem(pGenCred);
112  }
113  else
114  {
115     AuthContext._AuthFlag = FALSE;
116  }
117
118  XPL_LOG_DM_SESS_Debug(("CheckCredentials Exit\n"));
119}
120
121
122 DMString DMServerAuthentication::GetPreferredProfilePath( const DMString& strAccName, const DMMap<DMString, UINT32>& dmAuthProfiles )
123{
124
125  DMString  strAAuthPrefURI( ::XPL_DM_GetEnv( SYNCML_DM_DMACC_ROOT_PATH ) + DMString( DM_STR_SLASH ) + strAccName + DM_STR_SLASH + DM_AAUTHPREF );
126  DMString  strPreferredProfilePath;
127  DMGetData oAuthPref;
128
129  XPL_LOG_DM_SESS_Debug(("GetPrefferdProfilePath Entered\n"));
130
131  if( SYNCML_DM_SUCCESS == dmTreeObj.Get( strAAuthPrefURI, oAuthPref, SYNCML_DM_REQUEST_TYPE_INTERNAL ) )
132  {
133   DMString  strAuthPref( oAuthPref.getCharData() );
134
135    if( strAuthPref.length() != 0 )
136    {
137      for( DMMap<DMString, UINT32>::POS pos = dmAuthProfiles.begin();pos != dmAuthProfiles.end(); ++pos )
138      {
139        DMGetData oAuthType;
140        if( SYNCML_DM_SUCCESS == dmTreeObj.Get(dmAuthProfiles.get_key(pos)+DM_STR_SLASH DM_AAUTHTYPE,oAuthType,SYNCML_DM_REQUEST_TYPE_INTERNAL))
141        {
142          DMString strCurrentAuthType = oAuthType.getCharData();
143
144          if( 0 == strCurrentAuthType.CompareNoCase( strAuthPref ) )
145          {
146            strPreferredProfilePath = dmAuthProfiles.get_key( pos );
147            break;
148          }
149        }
150      }
151    }
152  }
153
154  return strPreferredProfilePath;
155}
156
157 SYNCML_DM_RET_STATUS_T DMServerAuthentication::TryProfile_1_1( const DMString& strAccName, const DMString& strProlilePath,SYNCML_DM_AuthContext_T& AuthContext )
158{
159       SYNCML_DM_RET_STATUS_T  result = SYNCML_DM_SUCCESS;
160
161
162       DMGetData oAuthSecret;
163       DMGetData oAuthData;
164
165       XPL_LOG_DM_SESS_Debug(("TryProfile_1_1 Entered\n"));
166
167       result = dmTreeObj.Get( strProlilePath + DM_STR_SLASH DM_SERVERPW, oAuthSecret, SYNCML_DM_REQUEST_TYPE_INTERNAL );
168       if( SYNCML_DM_SUCCESS != result )
169            return result;
170       result = dmTreeObj.Get( strProlilePath + DM_STR_SLASH DM_SERVERNONCE, oAuthData, SYNCML_DM_REQUEST_TYPE_INTERNAL );
171       if( SYNCML_DM_SUCCESS != result )
172            return result;
173
174       DMString device_id;
175       result = SYNCML_DM_BuildPackage::GetDeviceID( device_id );
176       if( SYNCML_DM_SUCCESS != result )
177            return result;
178
179       DMString password( oAuthSecret.getCharData() );
180
181       result = SYNCML_DM_BuildPackage::GetServerAuthValues( device_id, AuthContext._pServerId, password );
182
183       if( SYNCML_DM_SUCCESS != result )
184            return result;
185
186       CheckCredentials( AuthContext, password, oAuthData.m_oData, TRUE );
187
188       return result;
189}
190
191
192SYNCML_DM_RET_STATUS_T DMServerAuthentication::TryProfile_1_2( const DMString& strAccName,const DMString& strProlilePath, SYNCML_DM_AuthContext_T&  AuthContext )
193{
194  SYNCML_DM_RET_STATUS_T  result = SYNCML_DM_SUCCESS;
195
196  XPL_LOG_DM_SESS_Debug(("TryProfile_1_2 Entered\n"));
197  for( ; ; )
198  {
199    DMGetData oAuthLevel;
200    result = dmTreeObj.Get( strProlilePath + DM_STR_SLASH DM_AAUTHLEVEL, oAuthLevel, SYNCML_DM_REQUEST_TYPE_INTERNAL );
201    if( SYNCML_DM_SUCCESS != result ) break;
202
203    // DM: filter out server credentials only
204    if( 0 != DMString( oAuthLevel.getCharData()).CompareNoCase( DM_AUTHLEVEL_SRVCRED ) ) break;
205
206    DMGetData oAuthSecret;
207    DMGetData oAuthData;
208
209    result = dmTreeObj.Get( strProlilePath + DM_STR_SLASH DM_AAUTHSECRET, oAuthSecret, SYNCML_DM_REQUEST_TYPE_INTERNAL );
210    if( SYNCML_DM_SUCCESS != result ) break;
211
212    result = dmTreeObj.Get( strProlilePath + DM_STR_SLASH DM_AAUTHDATA, oAuthData, SYNCML_DM_REQUEST_TYPE_INTERNAL );
213    if( SYNCML_DM_SUCCESS != result ) break;
214
215    DMString device_id;
216    result = SYNCML_DM_BuildPackage::GetDeviceID( device_id );
217    if( SYNCML_DM_SUCCESS != result ) break;
218
219    DMString password( oAuthSecret.getCharData() );
220
221    result = SYNCML_DM_BuildPackage::GetServerAuthValues( device_id,AuthContext._pServerId, password );
222
223    if( SYNCML_DM_SUCCESS != result ) break;
224
225    CheckCredentials( AuthContext, password, oAuthData.m_oData, TRUE ); // decode nonce before calculating digest
226    if ( !AuthContext._AuthFlag )
227    {
228       XPL_LOG_DM_SESS_Warn(("CheckCredentials: Failed check credentials with decoding\n"));
229
230       CheckCredentials( AuthContext, password, oAuthData.m_oData, FALSE ); // do NOT decode nonce before calculating digest
231       if ( !AuthContext._AuthFlag )
232       {
233          XPL_LOG_DM_SESS_Warn(("CheckCredentials: Failed check credentials without decoding\n"));
234
235          oAuthData.m_oData.assign(SERVER_RESYNC_NONCE);
236          BOOLEAN bAuthFlag1 = FALSE;
237          BOOLEAN bAuthFlag2 = FALSE;
238
239          CheckCredentials( AuthContext, password, oAuthData.m_oData, TRUE );
240          bAuthFlag1 = AuthContext._AuthFlag;
241
242          if( !bAuthFlag1 )
243          {
244             CheckCredentials( AuthContext, password, oAuthData.m_oData, FALSE );
245             bAuthFlag2 = AuthContext._AuthFlag;
246          }
247
248          if ( bAuthFlag1 || bAuthFlag2 )
249          {
250             XPL_LOG_DM_SESS_Warn(("Nonce Resynchronization request detected\n"));
251             DMNode *pNode = dmTreeObj.FindNodeByURI(strProlilePath + DM_STR_SLASH DM_AAUTHDATA);
252             if ( NULL == pNode || NULL == pNode->getData() )
253             {
254                XPL_LOG_DM_SESS_Error(("Failed to reset server nonce!\n"));
255                AuthContext._AuthFlag = false;
256                result = SYNCML_DM_SESSION_AUTH_FAIL;
257                break;
258             }
259             pNode->getData()->assign(SERVER_RESYNC_NONCE);
260          }
261          else {
262             result = SYNCML_DM_SESSION_AUTH_FAIL;
263          }
264       }
265    }
266    break;
267  }
268
269  return result;
270}
271
272
273SYNCML_DM_RET_STATUS_T DMServerAuthentication::AuthenticateServer (SYNCML_DM_AuthContext_T& AuthContext)
274{
275  XPL_LOG_DM_SESS_Debug(("AutenticateServer Entered\n"));
276
277  SYNCML_DM_RET_STATUS_T  result = SYNCML_DM_SUCCESS;
278  CPCHAR szDMAccRootPath = ::XPL_DM_GetEnv( SYNCML_DM_DMACC_ROOT_PATH );
279  CPCHAR szServerIdNodeName = ::XPL_DM_GetEnv( SYNCML_DM_NODENAME_SERVERID );
280
281  /* Get the DMAcc node using the ServerId.*/
282  DMString strAccName;
283
284  AuthContext._AuthFlag = FALSE;
285
286  if(!dmTreeObj.GetParentOfKeyValue( AuthContext._pServerId, szServerIdNodeName, szDMAccRootPath, strAccName ) )
287  {
288    return (SYNCML_DM_FAIL);
289  }
290
291  if( strAccName == NULL)
292  {
293    /* The ServerId was not found in the DM Tree.*/
294    return (SYNCML_DM_FAIL);
295  }
296
297  if ( dmTreeObj.IsVersion_12()  )
298  {
299        DMString strAppAuthPath = szDMAccRootPath + DMString( DM_STR_SLASH ) + strAccName.c_str() + DM_STR_SLASH + DM_APPAUTH;
300        DMMap<DMString, UINT32> dmAuthProfiles;
301
302        result = dmTreeObj.getChildren( strAppAuthPath, dmAuthProfiles, DMTNM_NODE_INTERIOR,SYNCML_DM_REQUEST_TYPE_INTERNAL );
303        if( SYNCML_DM_SUCCESS == result )
304        {
305            DMString strPreferredProfilePath = GetPreferredProfilePath( strAccName, dmAuthProfiles );
306
307            if( strPreferredProfilePath.length() != 0 )
308            {
309                result = TryProfile_1_2( strAccName, strPreferredProfilePath, AuthContext );
310                if ( SYNCML_DM_SUCCESS != result )
311                {
312                    dmFreeGetMap(dmAuthProfiles);
313                    return result;
314                }
315            }
316
317            if( !AuthContext._AuthFlag )
318            {
319             // DM: preferred method didn't work. Try all profiles
320                for( DMMap<DMString, UINT32>::POS pos = dmAuthProfiles.begin(); pos != dmAuthProfiles.end(); ++pos )
321                {
322                    DMString strProfilePath = dmAuthProfiles.get_key( pos );
323
324                    // DM: skip preferred profile
325                    if( strPreferredProfilePath != strProfilePath )
326                    {
327                        result = TryProfile_1_2( strAccName, strProfilePath, AuthContext );
328                        if( ( SYNCML_DM_SUCCESS != result ) || AuthContext._AuthFlag ) break;
329                    }
330                }
331            }
332
333            dmFreeGetMap(dmAuthProfiles);
334        }
335    }
336    else
337    {
338        result = TryProfile_1_1( strAccName, szDMAccRootPath + DMString( DM_STR_SLASH ) + strAccName.c_str(),AuthContext );
339    }
340
341    return result;
342}
343