Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
sampleauthprovider.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2011 John Selbie
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 #include "commonincludes.hpp"
18 #include <openssl/hmac.h>
19 #include "stuncore.h"
20 #include "stunsocket.h"
21 #include "stunsocketthread.h"
22 #include "server.h"
23 #include "sampleauthprovider.h"
24 
25 
26 static const char* c_szPrivateKey = "Change this string if you are going to use this code";
27 static const char* c_szRealm = "YourRealmNameHere";
28 
29 
30 static HRESULT LookupPassword(bool fWithRealm, const char* pszUserName, const char* pszRealm, char* pszPassword)
31 {
32  const char* users[] = {"bruce", "steve", "nicko", "dave", "adrian"};
33  const char* passwords[] = {"AcesHigh", "2MinToMid", "fearofthedark!", "#ofthebeast", "Run2TheHills" };
34 
35  if (fWithRealm)
36  {
37  if ((pszRealm == NULL) || (strcmp(pszRealm, c_szRealm)))
38  {
39  return E_FAIL;
40  }
41  }
42 
43  if (pszUserName == NULL)
44  {
45  return E_FAIL;
46  }
47 
48  for (size_t index = 0; index < ARRAYSIZE(users); index++)
49  {
50  if (strcmp(pszUserName, users[index]))
51  {
52  continue;
53  }
54 
55  strcpy(pszPassword, passwords[index]);
56  return S_OK;
57  }
58 
59  return E_FAIL;
60 }
61 
62 
63 
64 
66 {
67  // if you want to authenticate in "short term credential mode", then this function needs to return
68  // a password for the passed in user name
69 
70  // short-term example
71 
72  // indicate to the server we are returning a short-term credential so it knows to use
73  // only the pResponse->szPassword field for computing the message integrity
74  pResponse->authCredMech = AuthCredShortTerm;
75 
76  if (pAuthAttributes->fMessageIntegrityPresent == false)
77  {
78  // RFC 5389 indicates to send back a "400" if there is no message integrity. That's
79  // what "Reject" will signal to the server to respond with
80  pResponse->responseType = Reject;
81  return S_OK;
82  }
83 
84  if (SUCCEEDED(LookupPassword(false, pAuthAttributes->szUser, NULL, pResponse->szPassword)))
85  {
86  // Returning "AllowConditional" indicates that the request can be accepted if and only if the
87  // message integrity attribute can be validated with the value placed into pResponse->szPassword
88  pResponse->responseType = AllowConditional;
89  return S_OK;
90  }
91 
92  // If it's not a valid user (or no password could be found), just return Unauthorized.
93  // This will result in a 401 getting sent back
94  pResponse->responseType = Unauthorized;
95 
96  return S_OK;
97 }
98 
100 {
101  HRESULT hr = S_OK;
102 
103  pResponse->authCredMech = AuthCredLongTerm;
104 
105  // Go ahead and generate a new nonce and set the realm.
106  // The realm and nonce attributes will only get sent back to the client when there is an auth error
107  CreateNonce(pResponse->szNonce);
108  strcpy(pResponse->szRealm, c_szRealm);
109 
110  // if we're missing any authentication attributes, then just return back a 401.
111  // This will trigger the server to send back the nonce and realm attributes to the client within the 401 resposne
112  if ((pAuthAttributes->fMessageIntegrityPresent == false) || (pAuthAttributes->szNonce[0] == 0) || (pAuthAttributes->szUser[0] == 0))
113  {
114  pResponse->responseType = Unauthorized;
115  return S_OK;
116  }
117 
118  // copy the user's password into szPassword
119  hr = LookupPassword(true, pAuthAttributes->szUser, pAuthAttributes->szNonce, pResponse->szPassword);
120  if (FAILED(hr))
121  {
122  // if not a valid user, same as before. Just send back a 401
123  pResponse->responseType = Unauthorized;
124  return S_OK;
125 
126  }
127 
128  // validate the nonce
129  if (FAILED(ValidateNonce(pAuthAttributes->szNonce)))
130  {
131  pResponse->responseType = StaleNonce;
132  return S_OK;
133  }
134 
135  // returning "AllowConditional" indicates that the request can be accepted if and only if the
136  // message integrity attribute can be validated with the value placed into pResponse->szPassword
137  pResponse->responseType = AllowConditional;
138 
139  return S_OK;
140 }
141 
142 
143 
144 
145 void CLongTermAuth::HmacToString(uint8_t* hmacresult, char* pszResult)
146 {
147  sprintf(pszResult, "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
148  hmacresult[0], hmacresult[1], hmacresult[2], hmacresult[3], hmacresult[4],
149  hmacresult[5], hmacresult[6], hmacresult[7], hmacresult[8], hmacresult[9],
150  hmacresult[10], hmacresult[11], hmacresult[12], hmacresult[13], hmacresult[14],
151  hmacresult[15], hmacresult[16], hmacresult[17], hmacresult[18], hmacresult[19]);
152 }
153 
155 {
156  // This is a sample "nonce provider". Our implementation of "nonce" is just a a string
157  // indicating a timestamp followed by an HMAC hash of the timestamp.
158  // Validation of a nonce is to just to make sure the timestamp isn't more than a couple
159  // of minutes old and that the hmac hash matches
160 
161  // If you use this code, make sure you change the value of c_szPrivateKey!
162 
163  time_t thetime = time(NULL);
164  uint8_t hmacresult[20] = {};
165  char szHMAC[20*2+1];
166  char szTime[sizeof(time_t)*4];
167  unsigned int len = ARRAYSIZE(hmacresult);
168 
169  sprintf(szTime, "%u:", (unsigned int)thetime);
170 
171  HMAC(::EVP_sha1(), (unsigned char*)c_szPrivateKey, strlen(c_szPrivateKey), (unsigned char*)szTime, strlen(szTime), hmacresult, &len);
172 
173  HmacToString(hmacresult, szHMAC);
174 
175  strcpy(pszNonce, szTime);
176  strcat(pszNonce, szHMAC);
177 
178  return S_OK;
179 }
180 
182 {
183  time_t thecurrenttime = time(NULL);
184  time_t thetime;
185  uint8_t hmacresult[20] = {};
186  char szHMAC[20*2+1];
187  char szNonce[100];
188  char *pRightHalf = NULL;
189  time_t diff;
190  unsigned int len = ARRAYSIZE(hmacresult);
191 
192  strncpy(szNonce, pszNonce, ARRAYSIZE(szNonce));
193  szNonce[ARRAYSIZE(szNonce)-1] = 0;
194 
195  pRightHalf = strstr(szNonce, ":");
196 
197  if (pRightHalf == NULL)
198  {
199  return E_FAIL;
200  }
201 
202  *pRightHalf++ = 0;
203 
204  thetime = atoi(szNonce);
205 
206  diff = thecurrenttime - thetime;
207  if (((thecurrenttime - thetime) > 120) || (diff < 0))
208  {
209  // nonce is more than 2 minutes old - reject
210  return E_FAIL;
211  }
212 
213  // nonce timestamp is valid, but was it signed by this server?
214  HMAC(::EVP_sha1(), (unsigned char*)c_szPrivateKey, strlen(c_szPrivateKey), (unsigned char*)szNonce, strlen(szNonce), hmacresult, &len);
215  HmacToString(hmacresult, szHMAC);
216  if (strcmp(szHMAC, pRightHalf))
217  {
218  return E_FAIL;
219  }
220 
221  return S_OK;
222 }
223 
224 
225 
226 
227 
228 
229 
AuthResponseType responseType
Definition: stunauth.h:54
#define S_OK
Definition: hresult.h:46
virtual HRESULT DoAuthCheck(AuthAttributes *pAuthAttributes, AuthResponse *pResponse)
char szUser[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:29
void HmacToString(uint8_t *hmacvalue, char *pszResult)
HRESULT CreateNonce(char *pszNonce)
char szNonce[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:31
bool fMessageIntegrityPresent
Definition: stunauth.h:33
AuthCredentialMechanism authCredMech
Definition: stunauth.h:55
#define ARRAYSIZE(arr)
HRESULT ValidateNonce(char *pszNonce)
static const char * c_szPrivateKey
#define SUCCEEDED(hr)
Definition: hresult.h:28
char szPassword[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:57
char szNonce[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:59
int32_t HRESULT
Definition: hresult.h:22
static const char * c_szRealm
static HRESULT LookupPassword(bool fWithRealm, const char *pszUserName, const char *pszRealm, char *pszPassword)
virtual HRESULT DoAuthCheck(AuthAttributes *pAuthAttributes, AuthResponse *pResponse)
#define E_FAIL
Definition: hresult.h:56
#define FAILED(hr)
Definition: hresult.h:29
char szRealm[MAX_STUN_AUTH_STRING_SIZE+1]
Definition: stunauth.h:58