Fossil

Check-in [9a23c348b1]
Login

Check-in [9a23c348b1]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Infrastructure in place on the client side to encrypt sync traffic. This is mostly untested so far because we do not yet have a server that understands encrypted traffic.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1: 9a23c348b1bb1daa7a7a8517f1bc27862a2655c9
User & Date: drh 2009-03-27 14:32:33.000
Context
2009-03-29
11:41
If no userid is specified in the URL then the client omits the login card from its request. ... (Closed-Leaf check-in: 09d6a6eb16 user: drh tags: experimental)
2009-03-27
14:32
Infrastructure in place on the client side to encrypt sync traffic. This is mostly untested so far because we do not yet have a server that understands encrypted traffic. ... (check-in: 9a23c348b1 user: drh tags: experimental)
2009-03-26
23:26
Completed implementation of utility functions to encrypt and decrypt blobs. ... (check-in: bf16ab9b7b user: drh tags: experimental)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/blob.c.
927
928
929
930
931
932
933


934
935
936
937
938
939
940

941
942
943
944
945
946
947
** prior to the current cursor position is ignored.
**
** Because of the prepended nonce, the output will be 20 bytes larger
** than the input.
**
** pOut should be initialized prior to invoking this routine.  pOut might
** already contain other content.  The encryption is appended to pOut.


*/
void blob_encrypt(Blob *pIn, const char *zPassword, Blob *pOut){
  char *aNonce;
  char *aIn;
  int nIn;
  char *aOut;


  aIn = pIn->aData;
  aIn += pIn->iCursor;
  nIn = pIn->nUsed - pIn->iCursor;
  if( nIn<=0 ) return;
  blob_resize(pOut, pOut->nUsed+nIn+N_NONCE);
  aOut = pOut->aData;
  aOut += pOut->iCursor;







>
>







>







927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
** prior to the current cursor position is ignored.
**
** Because of the prepended nonce, the output will be 20 bytes larger
** than the input.
**
** pOut should be initialized prior to invoking this routine.  pOut might
** already contain other content.  The encryption is appended to pOut.
** The encryption cannot be done in place; pOut cannot be the same blob
** as pIn.
*/
void blob_encrypt(Blob *pIn, const char *zPassword, Blob *pOut){
  char *aNonce;
  char *aIn;
  int nIn;
  char *aOut;

  assert( pIn!=pOut );
  aIn = pIn->aData;
  aIn += pIn->iCursor;
  nIn = pIn->nUsed - pIn->iCursor;
  if( nIn<=0 ) return;
  blob_resize(pOut, pOut->nUsed+nIn+N_NONCE);
  aOut = pOut->aData;
  aOut += pOut->iCursor;
959
960
961
962
963
964
965
966


967
968
969
970
971
972
973
974
975
976
977

978

979
980
981





982
983
984
985
986
987
988
** used, the original unencrypted text will be written into pOut.
**
** Only that portion of pIn beginning at the current cursor location and
** extending to the end of the blob is decrypted.  Any content of pIn
** prior to the current cursor position is ignored.
**
** pOut should be initialized prior to invoking this routine.  pOut might
** already contain other content.  The encryption is appended to pOut.


*/
void blob_decrypt(Blob *pIn, const char *zPassword, Blob *pOut){
  char *aNonce;
  char *aIn;
  int nIn;
  char *aOut;

  aIn = pIn->aData;
  aNonce = aIn + pIn->iCursor;
  aIn = aNonce + N_NONCE;
  nIn = pIn->nUsed - pIn->iCursor - N_NONCE;

  blob_resize(pOut, pOut->iCursor + nIn);

  aOut = pOut->aData;
  aOut += pOut->iCursor;
  rc4_coder(zPassword, aNonce, N_NONCE, aIn, nIn, aOut);





}


/*
** COMMAND: test-encrypt PASSWORD PLAINTEXT CYPHERTEXT
*/
void encrypt_test_cmd(void){







|
>
>











>
|
>



>
>
>
>
>







962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
** used, the original unencrypted text will be written into pOut.
**
** Only that portion of pIn beginning at the current cursor location and
** extending to the end of the blob is decrypted.  Any content of pIn
** prior to the current cursor position is ignored.
**
** pOut should be initialized prior to invoking this routine.  pOut might
** already contain other content.  The decryption is appended to pOut
** starting at its current cursor position.  Decryption can be done
** in place; it is acceptable for pOut and pIn to be the same blob.
*/
void blob_decrypt(Blob *pIn, const char *zPassword, Blob *pOut){
  char *aNonce;
  char *aIn;
  int nIn;
  char *aOut;

  aIn = pIn->aData;
  aNonce = aIn + pIn->iCursor;
  aIn = aNonce + N_NONCE;
  nIn = pIn->nUsed - pIn->iCursor - N_NONCE;
  if( pOut!=pIn ){
    blob_resize(pOut, pOut->iCursor + nIn);
  }
  aOut = pOut->aData;
  aOut += pOut->iCursor;
  rc4_coder(zPassword, aNonce, N_NONCE, aIn, nIn, aOut);
  if( pOut==pIn ){
    pOut->nUsed = pOut->iCursor + nIn;
    aOut[nIn] = 0;
    pOut->iCursor = pOut->nUsed;
  }
}


/*
** COMMAND: test-encrypt PASSWORD PLAINTEXT CYPHERTEXT
*/
void encrypt_test_cmd(void){
Changes to src/http.c.
168
169
170
171
172
173
174

















175
176
177
178
179
180
181
182
183




184
185
186
187
188
189
190
191
192
193


194
195
196
197
198
199
200
      return 0;
    }
    blob_append(pBlob, rbuf, read);
  }
  return blob_size(pBlob);
}
#endif


















/*
** Make a single attempt to talk to the server.  Return TRUE on success
** and FALSE on a failure.
**
** pHeader contains the HTTP header.  pPayload contains the content.
** The content of the reply is written into pReply.  pReply is assumed
** to be uninitialized prior to this call.
**




** If an error occurs, this routine return false, resets pReply and
** closes the persistent connection, if any.
*/
static int http_send_recv(Blob *pHeader, Blob *pPayload, Blob *pReply){
  int closeConnection=1;   /* default to closing the connection */
  int rc;
  int iLength;
  int iHttpVersion;
  int i;
  int nRead;


  char zLine[2000];

  if( pSocket==0 && http_open_socket() ){
    return 0;
  }
  iLength = -1;
#ifdef __MINGW32__







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









>
>
>
>










>
>







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
      return 0;
    }
    blob_append(pBlob, rbuf, read);
  }
  return blob_size(pBlob);
}
#endif

/*
** zLine is a line from the header of an HTTP reply.  Find the
** "argument" to this header line.  The argument is the first non-whitespace
** character following the first ":" in the line.  The argument consists of
** non-whitespace and non-semicolon characters.  Zero-terminate the argument
** before returning.
*/
static char *header_arg(char *zIn){
  char *z;
  while( *zIn && *zIn!=':' ){ zIn++; }
  if( *zIn==':' ){ zIn++; }
  while( isspace(*zIn) ){ zIn++; }
  for(z=zIn; *z && *z!=';' && !isspace(*z); z++){}
  *z = 0;
  return zIn;
}

/*
** Make a single attempt to talk to the server.  Return TRUE on success
** and FALSE on a failure.
**
** pHeader contains the HTTP header.  pPayload contains the content.
** The content of the reply is written into pReply.  pReply is assumed
** to be uninitialized prior to this call.
**
** pPayload has already been compressed and/or encrypted prior to being
** passed into this function.  pReply will be decrypted and/or decompressed
** as required by this function, based on the Content-Type of the reply.
**
** If an error occurs, this routine return false, resets pReply and
** closes the persistent connection, if any.
*/
static int http_send_recv(Blob *pHeader, Blob *pPayload, Blob *pReply){
  int closeConnection=1;   /* default to closing the connection */
  int rc;
  int iLength;
  int iHttpVersion;
  int i;
  int nRead;
  int needDecrypt = 0;
  int needDecompress = 0;
  char zLine[2000];

  if( pSocket==0 && http_open_socket() ){
    return 0;
  }
  iLength = -1;
#ifdef __MINGW32__
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232








233

234
235
236
237
238
239
240
      if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
      if( rc!=200 ) goto write_err;
      if( iHttpVersion==0 ){
        closeConnection = 1;
      }else{
        closeConnection = 0;
      }
    } else if( strncasecmp(zLine, "content-length:", 15)==0 ){
      iLength = atoi(&zLine[16]);
    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
      for(i=12; isspace(zLine[i]); i++){}
      if( zLine[i]=='c' || zLine[i]=='C' ){
        closeConnection = 1;
      }else if( zLine[i]=='k' || zLine[i]=='K' ){
        closeConnection = 0;
      }








    }

  }
  if( iLength<0 ) goto write_err;
  nRead = socket_read_blob(pReply, pSocket, iLength);
#else
  rc = fwrite(blob_buffer(pHeader), 1, blob_size(pHeader), pSocket);
  if( rc!=blob_size(pHeader) ) goto write_err;
  rc = fwrite(blob_buffer(pPayload), 1, blob_size(pPayload), pSocket);







|
|

|
|
|
|
|

>
>
>
>
>
>
>
>
|
>







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
      if( sscanf(zLine, "HTTP/1.%d %d", &iHttpVersion, &rc)!=2 ) goto write_err;
      if( rc!=200 ) goto write_err;
      if( iHttpVersion==0 ){
        closeConnection = 1;
      }else{
        closeConnection = 0;
      }
    }else if( strncasecmp(zLine,"content-length:",15)==0 ){
      iLength = atoi(header_arg(zLine));
    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
      int c = header_arg(zLine)[0];
      if( c=='c' || c=='C' ){
        closeConnection = 1;   /* Connection: close */
      }else if( c=='k' || c=='K' ){
        closeConnection = 0;   /* Connection: keep-alive */
      }
    }else if( strncasecmp(zLine, "content-type:", 13)==0 ){
      const char *zType = header_arg(zLine);
      if( strcmp(zType, "application/x-fossil")==0 ){
        needDecompress = 1;
        needDecrypt = 0;
      }else if( strcmp(zType, "application/x-fossil-secure")==0 ){
        needDecompress = 1;
        needDecrypt = 1;
      }
    }      
  }
  if( iLength<0 ) goto write_err;
  nRead = socket_read_blob(pReply, pSocket, iLength);
#else
  rc = fwrite(blob_buffer(pHeader), 1, blob_size(pHeader), pSocket);
  if( rc!=blob_size(pHeader) ) goto write_err;
  rc = fwrite(blob_buffer(pPayload), 1, blob_size(pPayload), pSocket);
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263








264

265
266
267
268
269
270
271
272
273
274
275






276
277
278
279
280
281
282
    closeConnection = 0;   /* Connection: keep-alive */
  }
  while( fgets(zLine, sizeof(zLine), pSocket) ){
    for(i=0; zLine[i] && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
    if( i==0 ) break;
    zLine[i] = 0;
    if( strncasecmp(zLine,"content-length:",15)==0 ){
      iLength = atoi(&zLine[16]);
    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
      for(i=12; isspace(zLine[i]); i++){}
      if( zLine[i]=='c' || zLine[i]=='C' ){
        closeConnection = 1;   /* Connection: close */
      }else if( zLine[i]=='k' || zLine[i]=='K' ){
        closeConnection = 0;   /* Connection: keep-alive */
      }








    }

  }
  if( iLength<0 ) goto write_err;
  nRead = blob_read_from_channel(pReply, pSocket, iLength);
#endif
  if( nRead!=iLength ){
    blob_reset(pReply);
    goto write_err;
  }
  if( closeConnection ){
    http_close();
  }






  return 1;  

write_err:
  http_close();
  return 0;
}








|

|
|

|


>
>
>
>
>
>
>
>
|
>











>
>
>
>
>
>







281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
    closeConnection = 0;   /* Connection: keep-alive */
  }
  while( fgets(zLine, sizeof(zLine), pSocket) ){
    for(i=0; zLine[i] && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
    if( i==0 ) break;
    zLine[i] = 0;
    if( strncasecmp(zLine,"content-length:",15)==0 ){
      iLength = atoi(header_arg(zLine));
    }else if( strncasecmp(zLine, "connection:", 11)==0 ){
      int c = header_arg(zLine)[0];
      if( c=='c' || c=='C' ){
        closeConnection = 1;   /* Connection: close */
      }else if( c=='k' || c=='K' ){
        closeConnection = 0;   /* Connection: keep-alive */
      }
    }else if( strncasecmp(zLine, "content-type:", 13)==0 ){
      const char *zType = header_arg(zLine);
      if( strcmp(zType, "application/x-fossil")==0 ){
        needDecompress = 1;
        needDecrypt = 0;
      }else if( strcmp(zType, "application/x-fossil-secure")==0 ){
        needDecompress = 1;
        needDecrypt = 1;
      }
    }      
  }
  if( iLength<0 ) goto write_err;
  nRead = blob_read_from_channel(pReply, pSocket, iLength);
#endif
  if( nRead!=iLength ){
    blob_reset(pReply);
    goto write_err;
  }
  if( closeConnection ){
    http_close();
  }
  if( needDecrypt ){
    blob_decrypt(pReply, g.urlPasswd, pReply);
  }
  if( needDecompress ){
    blob_uncompress(pReply, pReply);
  }
  return 1;  

write_err:
  http_close();
  return 0;
}

339
340
341
342
343
344
345




346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365


366
367
368
369
370
371
372
373
  }        
  blob_reset(&nonce);
  blob_reset(&pw);
  blob_reset(&sig);

  /* Construct the payload, which includes the login card.
  */




  if( g.fHttpTrace ){
    payload = login;
    blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
  }else{
    blob_compress2(&login, pSend, &payload);
    blob_reset(&login);
  }

  /* Construct the HTTP request header
  */
  blob_zero(&hdr);
  i = strlen(g.urlPath);
  if( i>0 && g.urlPath[i-1]=='/' ){
    zSep = "";
  }else{
    zSep = "/";
  }
  blob_appendf(&hdr, "POST %s%sxfer HTTP/1.1\r\n", g.urlPath, zSep);
  blob_appendf(&hdr, "Host: %s\r\n", g.urlHostname);
  blob_appendf(&hdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n");


  if( g.fHttpTrace ){
    blob_appendf(&hdr, "Content-Type: application/x-fossil-debug\r\n");
  }else{
    blob_appendf(&hdr, "Content-Type: application/x-fossil\r\n");
  }
  blob_appendf(&hdr, "Content-Length: %d\r\n\r\n", blob_size(&payload));

  if( g.fHttpTrace ){







>
>
>
>
|



















>
>
|







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  }        
  blob_reset(&nonce);
  blob_reset(&pw);
  blob_reset(&sig);

  /* Construct the payload, which includes the login card.
  */
  if( g.fHttpSecure && g.urlPasswd[0] ){
    blob_compress(pSend, pSend);
    payload = login;
    blob_encrypt(pSend, g.urlPasswd, &payload);
  }else if( g.fHttpTrace ){
    payload = login;
    blob_append(&payload, blob_buffer(pSend), blob_size(pSend));
  }else{
    blob_compress2(&login, pSend, &payload);
    blob_reset(&login);
  }

  /* Construct the HTTP request header
  */
  blob_zero(&hdr);
  i = strlen(g.urlPath);
  if( i>0 && g.urlPath[i-1]=='/' ){
    zSep = "";
  }else{
    zSep = "/";
  }
  blob_appendf(&hdr, "POST %s%sxfer HTTP/1.1\r\n", g.urlPath, zSep);
  blob_appendf(&hdr, "Host: %s\r\n", g.urlHostname);
  blob_appendf(&hdr, "User-Agent: Fossil/" MANIFEST_VERSION "\r\n");
  if( g.fHttpSecure && g.urlPasswd[0] ){
    blob_appendf(&hdr, "Content-Type: application/x-fossil-secure\r\n");
  }else if( g.fHttpTrace ){
    blob_appendf(&hdr, "Content-Type: application/x-fossil-debug\r\n");
  }else{
    blob_appendf(&hdr, "Content-Type: application/x-fossil\r\n");
  }
  blob_appendf(&hdr, "Content-Length: %d\r\n\r\n", blob_size(&payload));

  if( g.fHttpTrace ){
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416
417
418
419
420
  if( cnt>=2 ){
    fossil_fatal("connection to server failed");
  }
  blob_reset(&hdr);
  blob_reset(&payload);

  /* Process the reply.  pRecv contains only the payload of the
  ** reply message, not the header.

  */
  if( g.fHttpTrace ){
    printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pRecv));
  }else{
    blob_uncompress(pRecv, pRecv);
  }
}


/*
** Make sure the socket to the HTTP server is closed 
*/







|
>



<
<







454
455
456
457
458
459
460
461
462
463
464
465


466
467
468
469
470
471
472
  if( cnt>=2 ){
    fossil_fatal("connection to server failed");
  }
  blob_reset(&hdr);
  blob_reset(&payload);

  /* Process the reply.  pRecv contains only the payload of the
  ** reply message, not the header.  pRecv has already been decrypted
  ** and decompressed
  */
  if( g.fHttpTrace ){
    printf("HTTP RECEIVE:\n%s\n=======================\n", blob_str(pRecv));


  }
}


/*
** Make sure the socket to the HTTP server is closed 
*/
Changes to src/main.c.
60
61
62
63
64
65
66

67
68
69
70
71
72
73
  char *zRepositoryName;  /* Name of the repository database */
  int localOpen;          /* True if the local database is open */
  char *zLocalRoot;       /* The directory holding the  local database */
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if -sqltrace flag is present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
  int fHttpTrace;         /* Trace outbound HTTP requests */

  int fNoSync;            /* Do not do an autosync even.  --nosync */
  char *zPath;            /* Name of webpage being served */
  char *zExtra;           /* Extra path information past the webpage name */
  char *zBaseURL;         /* Full text of the URL being served */
  char *zTop;             /* Parent directory of zPath */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */







>







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  char *zRepositoryName;  /* Name of the repository database */
  int localOpen;          /* True if the local database is open */
  char *zLocalRoot;       /* The directory holding the  local database */
  int minPrefix;          /* Number of digits needed for a distinct UUID */
  int fSqlTrace;          /* True if -sqltrace flag is present */
  int fSqlPrint;          /* True if -sqlprint flag is present */
  int fHttpTrace;         /* Trace outbound HTTP requests */
  int fHttpSecure;        /* Encrypt sync traffic */
  int fNoSync;            /* Do not do an autosync even.  --nosync */
  char *zPath;            /* Name of webpage being served */
  char *zExtra;           /* Extra path information past the webpage name */
  char *zBaseURL;         /* Full text of the URL being served */
  char *zTop;             /* Parent directory of zPath */
  const char *zContentType;  /* The content type of the input HTTP request */
  int iErrPriority;       /* Priority of current error message */