Simple Geolocalization and Course Transmission Protocol (SGCTP)
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros Pages
transmit_tcp.cpp
Go to the documentation of this file.
1 // INDENTING (emacs/vi): -*- mode:c++; tab-width:2; c-basic-offset:2; intent-tabs-mode:nil; -*- ex: set tabstop=2 expandtab:
2 
3 /*
4  * Simple Geolocalization and Course Transmission Protocol (SGCTP)
5  * Copyright (C) 2014 Cedric Dufour <http://cedric.dufour.name>
6  *
7  * The Simple Geolocalization and Course Transmission Protocol (SGCTP) is
8  * free software:
9  * you can redistribute it and/or modify it under the terms of the GNU General
10  * Public License as published by the Free Software Foundation, Version 3.
11  *
12  * The Simple Geolocalization and Course Transmission Protocol (SGCTP) is
13  * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
14  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15  * PARTICULAR PURPOSE.
16  *
17  * See the GNU General Public License for more details.
18  */
19 
20 // C
21 #include <errno.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <arpa/inet.h>
25 #include <sys/socket.h>
26 #include <sys/time.h>
27 
28 // SGCTP
29 #include "sgctp/data.hpp"
30 #include "sgctp/payload.hpp"
31 #include "sgctp/payload_aes128.hpp"
32 #include "sgctp/principal.hpp"
33 #include "sgctp/transmit_tcp.hpp"
34 using namespace SGCTP;
35 
36 
37 //----------------------------------------------------------------------
38 // METHODS: CTransmit (implement/override)
39 //----------------------------------------------------------------------
40 
41 void CTransmit_TCP::setTimeout( int _iSocket,
42  double _fdTimeout )
43 {
44  // Set socket timeout
45  struct timeval __tTimeval;
46  double __fdSeconds;
47  __tTimeval.tv_usec = 1000000 * (int)modf( _fdTimeout, &__fdSeconds );
48  __tTimeval.tv_sec = (int)__fdSeconds;
49  setsockopt( _iSocket,
50  SOL_SOCKET, SO_RCVTIMEO,
51  &__tTimeval, sizeof( __tTimeval ) );
52  setsockopt( _iSocket,
53  SOL_SOCKET, SO_SNDTIMEO,
54  &__tTimeval, sizeof( __tTimeval ) );
55 
56  // Set transmission timeout
57  CTransmit::setTimeout( _iSocket,
58  _fdTimeout );
59 }
60 
61 int CTransmit_TCP::send( int _iSocket,
62  const void *_pBuffer,
63  int _iSize,
64  int _iFlags )
65 {
66  int __iReturn = ::send( _iSocket, _pBuffer, _iSize, _iFlags );
67  return
68  ( __iReturn >= 0 )
69  ? __iReturn
70  : -errno;
71 }
72 
73 int CTransmit_TCP::recv( int _iSocket,
74  void *_pBuffer,
75  int _iSize,
76  int _iFlags )
77 {
78  int __iReturn = ::recv( _iSocket, _pBuffer, _iSize, _iFlags );
79  return
80  ( __iReturn >= 0 )
81  ? __iReturn
82  : -errno;
83 }
84 
86 {
87  return CTransmit::allocBuffer();
88 }
89 
90 int CTransmit_TCP::serialize( int _iSocket,
91  const CData &_roData )
92 {
93  int __iReturn;
94  int __iExit;
95 
96  // Standard serialization
97  __iReturn = CTransmit::serialize( _iSocket, _roData );
98  if( __iReturn <= 0 )
99  return __iReturn;
100  __iExit = __iReturn;
101 
102  // Cryptographic key incrementation
103  switch( ePayloadType )
104  {
105 
106  case PAYLOAD_AES128:
107  __iReturn = ((CPayload_AES128*)poPayload)->incrCryptoKey();
108  if( __iReturn )
109  return __iReturn;
110  break;
111 
112  default:;
113 
114  }
115 
116  // Done
117  return __iExit;
118 }
119 
120 int CTransmit_TCP::unserialize( int _iSocket,
121  CData *_poData,
122  int _iMaxSize )
123 {
124  int __iReturn;
125  int __iExit;
126 
127  // Standard unserialization
128  __iReturn = CTransmit::unserialize( _iSocket, _poData, _iMaxSize );
129  if( __iReturn <= 0 )
130  return __iReturn;
131  __iExit = __iReturn;
132 
133  // Cryptographic key incrementation
134  switch( ePayloadType )
135  {
136 
137  case PAYLOAD_AES128:
138  __iReturn = ((CPayload_AES128*)poPayload)->incrCryptoKey();
139  if( __iReturn )
140  return __iReturn;
141  break;
142 
143  default:;
144 
145  }
146 
147  // Done
148  return __iExit;
149 }
150 
151 
152 //----------------------------------------------------------------------
153 // METHODS
154 //----------------------------------------------------------------------
155 
156 /* Cryptographic handshake and data exchange:
157  *
158  * CLIENT <-> SERVER
159  * setTimeout // prevent TCP-sockets-exhaustion DoS
160  * ---> ID, NonceC --->
161  * Key0 = key( password(ID), NonceC ) // prevent rainbow-table attacks
162  * <--- crypt( NonceS, Key0 ) <---
163  * Key = key( NonceS, NonceC ) // prevent (out-of-band) replay attacks
164  * ---> crypt( "#AUTH", Key ) --->
165  * Key = key'() // prevent (in-band) replay attacks
166  * <--- crypt( "#OK", key ) <---
167  * Key = key'()
168  * unsetTimeout
169  * <--> ... data exchange ... <-->
170  * Key = key'()
171  */
172 
173 int CTransmit_TCP::sendHandshake( int _iSocket )
174 {
175  int __iReturn;
176 
177  // Check resources
178  if( !poPayload )
179  return -ENODATA;
180  if( !pucBuffer )
181  {
182  __iReturn = alloc();
183  if( __iReturn < 0 )
184  return __iReturn;
185  }
186 
187  // Set socket timeout
188  setTimeout( _iSocket, 3.0 ); // prevent DoS
189 
190  // Error-catching block
191  int __iError = 1;
192  int __iPayloadSize = 0;
193  do
194  {
195  CData __oData;
196 
197  // Lookup principal
198  if( pcPrincipalsPath )
199  {
200  __iReturn = usePrincipal()->read( pcPrincipalsPath, oPrincipal.getID() );
201  if( __iReturn )
202  {
203  __iError = -EACCES;
204  break;
205  }
206  }
207 
208  // Create handshake
209 
210  // ... signature
211  memcpy( pucBuffer, "SGCTP", 5 );
212  __iPayloadSize += 5;
213 
214  // ... protocol version
215  *((uint8_t*)(pucBuffer+__iPayloadSize)) = PROTOCOL_VERSION;
216  __iPayloadSize += 1;
217 
218  // ... payload type
219  *((uint8_t*)(pucBuffer+__iPayloadSize)) = (uint8_t)ePayloadType;
220  __iPayloadSize += 1;
221 
222  // ... principal ID
223  *((uint32_t*)(pucBuffer+__iPayloadSize)) =
224  htonl( (uint32_t)( oPrincipal.getID() >> 32 ) );
225  __iPayloadSize += 4;
226  *((uint32_t*)(pucBuffer+__iPayloadSize)) =
227  htonl( (uint32_t)oPrincipal.getID() );
228  __iPayloadSize += 4;
229 
230  // Send handshake
231  __iReturn = send( _iSocket, pucBuffer, __iPayloadSize, MSG_EOR );
232  if( __iReturn != __iPayloadSize )
233  {
234  __iError =
235  ( __iReturn <= 0 )
236  ? __iReturn
237  : -EPROTO;
238  break;
239  }
240 
241  // Cryptographic session establishment
242  bool __bSendAuthPayload = false;
243  switch( ePayloadType )
244  {
245 
246  case PAYLOAD_AES128:
247  {
248  CPayload_AES128 *__poPayload_AES128 = (CPayload_AES128*)poPayload;
249 
250  // ... send client nonce
251  unsigned char __pucNonceClient[CPayload_AES128::CRYPTO_NONCE_SIZE];
252  __iReturn = CPayload_AES128::makeCryptoNonce( __pucNonceClient );
253  if( __iReturn )
254  {
255  __iError = __iReturn;
256  break;
257  }
258  __iReturn = send( _iSocket,
259  __pucNonceClient,
261  MSG_EOR );
262  if( __iReturn != CPayload_AES128::CRYPTO_NONCE_SIZE )
263  {
264  __iError =
265  ( __iReturn <= 0 )
266  ? __iReturn
267  : -EPROTO;
268  break;
269  }
270  __poPayload_AES128->makeCryptoKey( (unsigned char*)oPrincipal.getPassword(),
271  strlen( oPrincipal.getPassword() ),
272  __pucNonceClient );
273 
274  // ... receive (decrypted) server nonce (session token)
275  resetBuffer();
276  __oData.reset();
277  __iReturn = unserialize( _iSocket, &__oData, 48 );
278  if( __iReturn < 0 )
279  {
280  __iError = __iReturn;
281  break;
282  }
284  {
285  __iError = -EPROTO;
286  break;
287  }
288  __poPayload_AES128->makeCryptoKey( __oData.getData(),
290  __pucNonceClient );
291 
292  __bSendAuthPayload = true;
293  }
294  break;
295 
296  default:;
297  }
298  if( pcPrincipalsPath )
299  usePrincipal()->erasePassword(); // clear password from memory
300  if( __iError <= 0 )
301  break;
302 
303  // Send AUTH payload
304  if( __bSendAuthPayload )
305  {
306  __oData.reset();
307  __oData.setID( "#AUTH" );
308  __iReturn = serialize( _iSocket, __oData );
309  if( __iReturn <= 0 )
310  {
311  __iError = __iReturn;
312  break;
313  }
314  }
315 
316  // Receive OK payload
317  resetBuffer();
318  __oData.reset();
319  __iReturn = unserialize( _iSocket, &__oData, 32 );
320  if( __iReturn < 0 )
321  {
322  __iError = __iReturn;
323  break;
324  }
325  if( strcmp( __oData.getID(), "#OK" ) )
326  {
327  __iError = -EPROTO;
328  break;
329  }
330 
331  }
332  while( false ); // Error-catching block
333 
334  // Unset socket timeout
335  setTimeout( _iSocket, 0.0 );
336 
337  // Reset buffer (for reception)
338  resetBuffer();
339 
340  // Done
341  return
342  ( __iError <= 0 )
343  ? __iError
344  : __iPayloadSize;
345 }
346 
347 int CTransmit_TCP::recvHandshake( int _iSocket )
348 {
349  int __iReturn;
350 
351  // Check resources
352  if( !pucBuffer )
353  {
354  __iReturn = alloc();
355  if( __iReturn < 0 )
356  return __iReturn;
357  }
358 
359  // Set socket timeout
360  setTimeout( _iSocket, 3.0 ); // prevent DoS
361 
362  // Error-catching block
363  int __iError = 1;
364  int __iPayloadSize = 0;
365  do
366  {
367  CData __oData;
368 
369  // Receive handshake
370  resetBuffer();
371  __iReturn = recvBuffer( _iSocket, 15 );
372  if( __iReturn != 15 )
373  {
374  __iError =
375  ( __iReturn <= 0 )
376  ? __iReturn
377  : -EPROTO;
378  break;
379  }
380 
381  // Parse handshake
382  const unsigned char *__pucHandshakeBuffer = pullBuffer( 15 );
383 
384  // ... signature
385  if( memcmp( __pucHandshakeBuffer, "SGCTP", 5 ) )
386  {
387  __iError = -EPROTO;
388  break;
389  }
390  __iPayloadSize += 5;
391 
392  // ... protocol version
393  uint8_t __ui8tProtocolVersion =
394  *((uint8_t*)(__pucHandshakeBuffer+__iPayloadSize));
395  if( __ui8tProtocolVersion != PROTOCOL_VERSION )
396  {
397  __iError = -EPROTO;
398  break;
399  }
400  __iPayloadSize += 1;
401 
402  // ... payload type
403  uint8_t __ui8tPayloadType =
404  *((uint8_t*)(__pucHandshakeBuffer+__iPayloadSize));
405  __iPayloadSize += 1;
406 
407  // ... principal ID
408  uint64_t __ui64tPrincipalID =
409  ntohl( *((uint32_t*)(__pucHandshakeBuffer+__iPayloadSize)) );
410  __iPayloadSize += 4;
411  __ui64tPrincipalID |=
412  ntohl( *((uint32_t*)(__pucHandshakeBuffer+__iPayloadSize)) );
413  __iPayloadSize += 4;
414 
415  // Lookup principal
416  if( pcPrincipalsPath )
417  {
418  __iReturn = usePrincipal()->read( pcPrincipalsPath, __ui64tPrincipalID );
419  if( __iReturn )
420  {
421  __iError = -EACCES;
422  break;
423  }
424  }
425 
426  // Initialize payload
427  __iReturn = initPayload( __ui8tPayloadType );
428  if( __iReturn < 0 )
429  {
430  __iError = __iReturn;
431  break;
432  }
433 
434  // Cryptographic session establishment
435  bool __bRecvAuthPayload = false;
436  switch( __ui8tPayloadType )
437  {
438 
439  case PAYLOAD_AES128:
440  {
441  CPayload_AES128 *__poPayload_AES128 = (CPayload_AES128*)poPayload;
442 
443  // ... receive client nonce
444  unsigned char __pucNonceClient[CPayload_AES128::CRYPTO_NONCE_SIZE];
445  __iReturn = recvBuffer( _iSocket, CPayload_AES128::CRYPTO_NONCE_SIZE );
446  if( __iReturn != CPayload_AES128::CRYPTO_NONCE_SIZE )
447  {
448  __iError =
449  ( __iReturn <= 0 )
450  ? __iReturn
451  : -EPROTO;
452  break;
453  }
454  memcpy( __pucNonceClient,
457  __poPayload_AES128->makeCryptoKey( (unsigned char*)oPrincipal.getPassword(),
458  strlen( oPrincipal.getPassword() ),
459  __pucNonceClient );
460 
461  // ... send (encrypted) server nonce (session token)
462  unsigned char __pucNonceServer[CPayload_AES128::CRYPTO_NONCE_SIZE];
463  __iReturn = CPayload_AES128::makeCryptoNonce( __pucNonceServer );
464  if( __iReturn )
465  {
466  __iError = __iReturn;
467  break;
468  }
469  __oData.reset();
470  __oData.setData( __pucNonceServer, CPayload_AES128::CRYPTO_NONCE_SIZE );
471  __iReturn = serialize( _iSocket, __oData );
472  if( __iReturn <= 0 )
473  {
474  __iError = __iReturn;
475  break;
476  }
477  __poPayload_AES128->makeCryptoKey( __pucNonceServer,
479  __pucNonceClient );
480 
481  // ... clean-up
482  memset( __pucNonceServer, 0, CPayload_AES128::CRYPTO_NONCE_SIZE );
483 
484  __bRecvAuthPayload = true;
485  }
486  break;
487 
488  default:;
489  }
490  if( pcPrincipalsPath )
491  usePrincipal()->erasePassword(); // clear password from memory
492  if( __iError <= 0 )
493  break;
494 
495  // Receive AUTH payload
496  resetBuffer();
497  if( __bRecvAuthPayload )
498  {
499  __oData.reset();
500  __iReturn = unserialize( _iSocket, &__oData, 32 );
501  if( __iReturn < 0 || strcmp( __oData.getID(), "#AUTH" ) )
502  {
503  __oData.reset();
504  __oData.setID( "#KO" );
505  serialize( _iSocket, __oData );
506  __iError =
507  ( __iReturn <= 0 )
508  ? __iReturn
509  : -EPROTO;
510  break;
511  }
512  }
513 
514  // Send OK payload
515  {
516  __oData.reset();
517  __oData.setID( "#OK" );
518  __iReturn = serialize( _iSocket, __oData );
519  if( __iReturn <= 0 )
520  {
521  __iError = __iReturn;
522  break;
523  }
524  }
525 
526  }
527  while( false ); // Error-catching block
528 
529  // Unset socket timeout
530  setTimeout( _iSocket, 0.0 );
531 
532  // Reset buffer (for reception)
533  resetBuffer();
534 
535  // Done
536  return
537  ( __iError <= 0 )
538  ? __iError
539  : __iPayloadSize;
540 }
virtual void setTimeout(int _iSocket, double _fdTimeout)
Set the transmission (send/receive) timeout, in seconds.
static const uint8_t PROTOCOL_VERSION
Protocol version.
Definition: transmit.hpp:55
int makeCryptoKey(const unsigned char *_pucPassword, int _iPasswordLength, const unsigned char *_pucNonce)
Create cryptographic key (and seal)
uint64_t getID() const
Returns the principal ID.
Definition: principal.hpp:113
virtual int serialize(int _iDescriptor, const CData &_roData)
Serialize the given SGCTP data to the given descriptor.
Definition: transmit.cpp:212
const uint16_t getDataSize() const
Return the data size.
Definition: data.hpp:289
virtual int recv(int _iSocket, void *_pBuffer, int _iSize, int _iFlags)
Receive data from the given descriptor.
void setID(const char *_pcID)
Set the ID string (max. 127 characters)
Definition: data.cpp:206
int sendHandshake(int _iSocket)
Send the TCP handshake.
const char * getPassword() const
Returns the principal password.
Definition: principal.hpp:118
uint16_t setData(const unsigned char *_pucData, uint16_t _ui16tDataSize)
Set the data (max. 32767 symbols)
Definition: data.cpp:216
virtual int send(int _iSocket, const void *_pBuffer, int _iSize, int _iFlags)
Send data to the given descriptor.
virtual int alloc()
Allocate resources required for data transmission (un-/serialization)
CPrincipal oPrincipal
Principal.
Definition: parameters.hpp:56
virtual void setTimeout(int _iDescriptor, double _fdTimeout)
Set the transmission (send/receive) timeout, in seconds.
Definition: transmit.hpp:140
void reset(bool _bDataFree=true)
Reset (undefine) all data.
Definition: data.cpp:175
AES128-encrypted payload.
Definition: transmit.hpp:68
virtual int initPayload(uint8_t _ui8tPayloadType=PAYLOAD_RAW)
Associate and initialize payload object.
Definition: transmit.cpp:160
static const uint16_t CRYPTO_NONCE_SIZE
const unsigned char * pullBuffer(int _iSize)
Pull data from the transmission buffer.
Definition: transmit.cpp:121
int recvBuffer(int _iDescriptor, int _iSize)
Receive data from the given descriptor (and push them on the data buffer)
Definition: transmit.cpp:80
void erasePassword()
Erases the principal password (and nullifies memory)
Definition: principal.cpp:51
SGCTP data container.
Definition: data.hpp:44
CPayload * poPayload
Associated payload object.
Definition: transmit.hpp:91
EPayloadType ePayloadType
Associated payload type.
Definition: transmit.hpp:93
int recvHandshake(int _iSocket)
Receive the TCP handshake (and initialize internal resources: principal/payload)
CPrincipal * usePrincipal()
Return the principal (pointer)
Definition: parameters.hpp:84
void resetBuffer()
Reset the data buffer (clear all data)
Definition: transmit.cpp:146
unsigned char * pucBuffer
Transmission buffer.
Definition: transmit.hpp:79
static int makeCryptoNonce(unsigned char *_pucNonce)
Create cryptographic nonce.
virtual int unserialize(int _iDescriptor, CData *_poData, int _iMaxSize=0)
Unserialize the SGCTP data from the given descriptor.
Definition: transmit.cpp:256
void getData(unsigned char *_pucData, uint16_t *_pui16tDataSize) const
Return the data (max. 32767 symbols)
Definition: data.cpp:450
const char * getID() const
Return the ID string.
Definition: data.hpp:276
int read(const char *_pcPrincipalsPath, uint64_t _ui64tID)
Retrieves the principal paremeters from the given file.
Definition: principal.cpp:91
const char * pcPrincipalsPath
Principals (database) path (pointer to existing variable)
Definition: parameters.hpp:54
virtual int serialize(int _iSocket, const CData &_roData)
Serialize the given SGCTP data to the given descriptor.
virtual int unserialize(int _iSocket, CData *_poData, int _iMaxSize=0)
Unserialize the SGCTP data from the given descriptor.
AES128-encrypted SGCTP payload.
int allocBuffer()
Allocate resources required for data transmission.
Definition: transmit.cpp:66