~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to SSL/GSUnixSSLHandle.m

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/** Implementation for GSUnixSSLHandle for GNUStep
2
 
   Copyright (C) 1997-1999 Free Software Foundation, Inc.
3
 
 
4
 
   Written by:  Richard Frith-Macdonald <richard@brainstorm.co.uk>
5
 
   Date: 1997
6
 
 
7
 
   This file is part of the GNUstep Base Library.
8
 
 
9
 
   This library is free software; you can redistribute it and/or
10
 
   modify it under the terms of the GNU Library General Public
11
 
   License as published by the Free Software Foundation; either
12
 
   version 2 of the License, or (at your option) any later version.
13
 
 
14
 
   This library is distributed in the hope that it will be useful,
15
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
 
   Library General Public License for more details.
18
 
 
19
 
   You should have received a copy of the GNU Library General Public
20
 
   License along with this library; if not, write to the Free
21
 
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
22
 
   */
23
 
 
24
 
 
25
 
#include <config.h>
26
 
 
27
 
#if     defined(__WIN32__) || defined(_WIN32) || defined(__MS_WIN32__)
28
 
#ifndef __WIN32__
29
 
#define __WIN32__
30
 
#endif
31
 
#endif
32
 
 
33
 
#ifdef __MINGW32__
34
 
#ifndef __MINGW__
35
 
#define __MINGW__
36
 
#endif
37
 
#ifndef __WIN32__
38
 
#define __WIN32__
39
 
#endif
40
 
#endif
41
 
 
42
 
#if defined(__WIN32__)
43
 
#include <windows.h>
44
 
#define GNUSTEP_BASE_SOCKET_MESSAGE (WM_USER + 1)
45
 
#endif
46
 
 
47
 
  /* Because openssl uses `id' as variable name sometime,
48
 
     while it is an Objective-C reserved keyword. */
49
 
  #define id id_x_
50
 
  #include <openssl/ssl.h>
51
 
  #undef id
52
 
 
53
 
#include <GSConfig.h>
54
 
#include <Foundation/Foundation.h>
55
 
 
56
 
#include <gnustep/base/UnixFileHandle.h>
57
 
 
58
 
#if defined(__MINGW__)
59
 
#include <winsock2.h>
60
 
#else
61
 
#include <time.h>
62
 
#include <sys/time.h>
63
 
#include <sys/param.h>
64
 
#include <sys/socket.h>
65
 
#include <netinet/in.h>
66
 
#include <arpa/inet.h>
67
 
#include <signal.h>
68
 
#endif /* __MINGW__ */
69
 
 
70
 
#include <sys/file.h>
71
 
#include <sys/stat.h>
72
 
#include <sys/fcntl.h>
73
 
#include <sys/ioctl.h>
74
 
#ifdef  __svr4__
75
 
#include <sys/filio.h>
76
 
#endif
77
 
#include <netdb.h>
78
 
#include <string.h>
79
 
#if HAVE_UNISTD_H
80
 
#include <unistd.h>
81
 
#endif
82
 
#include <errno.h>
83
 
 
84
 
// Maximum data in single I/O operation
85
 
#define NETBUF_SIZE     4096
86
 
 
87
 
// Key to info dictionary for operation mode.
88
 
static NSString*        NotificationKey = @"NSFileHandleNotificationKey";
89
 
 
90
 
 
91
 
 
92
 
@interface      GSUnixSSLHandle : UnixFileHandle <GCFinalization>
93
 
{
94
 
  SSL_CTX       *ctx;
95
 
  SSL           *ssl;
96
 
  BOOL          connected;
97
 
}
98
 
- (BOOL) sslConnect;
99
 
- (void) sslDisconnect;
100
 
- (void) sslSetCertificate: (NSString*)certFile
101
 
                privateKey: (NSString*)privateKey
102
 
                 PEMpasswd: (NSString*)PEMpasswd;
103
 
@end
104
 
 
105
 
@implementation GSUnixSSLHandle
106
 
+ (void) initialize
107
 
{
108
 
  if (self == [GSUnixSSLHandle class])
109
 
    {
110
 
      SSL_library_init();
111
 
    }
112
 
}
113
 
 
114
 
- (NSData*) availableData
115
 
{
116
 
  char          buf[NETBUF_SIZE];
117
 
  NSMutableData *d;
118
 
  int           len;
119
 
 
120
 
  [self checkRead];
121
 
  if (isNonBlocking == YES)
122
 
    [self setNonBlocking: NO];
123
 
  d = [NSMutableData dataWithCapacity: 0];
124
 
  if (isStandardFile)
125
 
    {
126
 
      while ((len = read(descriptor, buf, sizeof(buf))) > 0)
127
 
        {
128
 
          [d appendBytes: buf length: len];
129
 
        }
130
 
    }
131
 
  else
132
 
    {
133
 
      if (connected)
134
 
        {
135
 
          if ((len = SSL_read(ssl, buf, sizeof(buf))) > 0)
136
 
            {
137
 
              [d appendBytes: buf length: len];
138
 
            }
139
 
        }
140
 
      else
141
 
        {
142
 
          if ((len = read(descriptor, buf, sizeof(buf))) > 0)
143
 
            {
144
 
              [d appendBytes: buf length: len];
145
 
            }
146
 
        }
147
 
    }
148
 
  if (len < 0)
149
 
    {
150
 
      [NSException raise: NSFileHandleOperationException
151
 
                  format: @"unable to read from descriptor - %s",
152
 
                  GSLastErrorStr(errno)];
153
 
    }
154
 
  return d;
155
 
}
156
 
 
157
 
- (void) closeFile
158
 
{
159
 
  [self sslDisconnect];
160
 
  [super closeFile];
161
 
}
162
 
 
163
 
- (void) gcFinalize
164
 
{
165
 
  [self sslDisconnect];
166
 
  [super gcFinalize];
167
 
}
168
 
 
169
 
- (NSData*) readDataOfLength: (unsigned)len
170
 
{
171
 
  NSMutableData *d;
172
 
  int           got;
173
 
 
174
 
  [self checkRead];
175
 
  if (isNonBlocking == YES)
176
 
    [self setNonBlocking: NO];
177
 
  if (len <= 65536)
178
 
    {
179
 
      char      *buf;
180
 
 
181
 
      buf = NSZoneMalloc(NSDefaultMallocZone(), len);
182
 
      d = [NSMutableData dataWithBytesNoCopy: buf length: len];
183
 
      if ((got = SSL_read(ssl, [d mutableBytes], len)) < 0)
184
 
        {
185
 
          [NSException raise: NSFileHandleOperationException
186
 
                      format: @"unable to read from descriptor - %s",
187
 
                      GSLastErrorStr(errno)];
188
 
        }
189
 
      [d setLength: got];
190
 
    }
191
 
  else
192
 
    {
193
 
      char      buf[NETBUF_SIZE];
194
 
 
195
 
      d = [NSMutableData dataWithCapacity: 0];
196
 
      do
197
 
        {
198
 
          int   chunk = len > sizeof(buf) ? sizeof(buf) : len;
199
 
 
200
 
          if (connected)
201
 
            {
202
 
              got = SSL_read(ssl, buf, chunk);
203
 
            }
204
 
          else
205
 
            {
206
 
              got = read(descriptor, buf, chunk);
207
 
            }
208
 
          if (got > 0)
209
 
            {
210
 
              [d appendBytes: buf length: got];
211
 
              len -= got;
212
 
            }
213
 
          else if (got < 0)
214
 
            {
215
 
              [NSException raise: NSFileHandleOperationException
216
 
                          format: @"unable to read from descriptor - %s",
217
 
                          GSLastErrorStr(errno)];
218
 
            }
219
 
        }
220
 
      while (len > 0 && got > 0);
221
 
    }
222
 
  return d;
223
 
}
224
 
 
225
 
- (NSData*) readDataToEndOfFile
226
 
{
227
 
  char          buf[NETBUF_SIZE];
228
 
  NSMutableData *d;
229
 
  int           len;
230
 
 
231
 
  [self checkRead];
232
 
  if (isNonBlocking == YES)
233
 
    [self setNonBlocking: NO];
234
 
  d = [NSMutableData dataWithCapacity: 0];
235
 
  if (connected)
236
 
    {
237
 
      while ((len = SSL_read(ssl, buf, sizeof(buf))) > 0)
238
 
        {
239
 
          [d appendBytes: buf length: len];
240
 
        }
241
 
    }
242
 
  else
243
 
    {
244
 
      while ((len = read(descriptor, buf, sizeof(buf))) > 0)
245
 
        {
246
 
          [d appendBytes: buf length: len];
247
 
        }
248
 
    }
249
 
  if (len < 0)
250
 
    {
251
 
      [NSException raise: NSFileHandleOperationException
252
 
                  format: @"unable to read from descriptor - %s",
253
 
                  GSLastErrorStr(errno)];
254
 
    }
255
 
  return d;
256
 
}
257
 
 
258
 
- (void) receivedEvent: (void*)data
259
 
                  type: (RunLoopEventType)type
260
 
                 extra: (void*)extra
261
 
               forMode: (NSString*)mode
262
 
{
263
 
  NSString      *operation;
264
 
 
265
 
  if (isNonBlocking == NO)
266
 
    [self setNonBlocking: YES];
267
 
  if (type == ET_RDESC)
268
 
    {
269
 
      operation = [readInfo objectForKey: NotificationKey];
270
 
      if (operation == NSFileHandleConnectionAcceptedNotification)
271
 
        {
272
 
          struct sockaddr_in    buf;
273
 
          int                   desc;
274
 
          int                   blen = sizeof(buf);
275
 
 
276
 
          desc = accept(descriptor, (struct sockaddr*)&buf, &blen);
277
 
          if (desc < 0)
278
 
            {
279
 
              NSString  *s;
280
 
 
281
 
              s = [NSString stringWithFormat: @"Accept attempt failed - %s",
282
 
                      GSLastErrorStr(errno)];
283
 
              [readInfo setObject: s forKey: GSFileHandleNotificationError];
284
 
            }
285
 
          else
286
 
            { // Accept attempt completed.
287
 
              UnixFileHandle            *h;
288
 
              struct sockaddr_in        sin;
289
 
              int                       size = sizeof(sin);
290
 
 
291
 
              h = [[GSUnixSSLHandle alloc] initWithFileDescriptor: desc
292
 
                                                   closeOnDealloc: YES];
293
 
              getpeername(desc, (struct sockaddr*)&sin, &size);
294
 
              [h setAddr: &sin];
295
 
              [readInfo setObject: h
296
 
                           forKey: NSFileHandleNotificationFileHandleItem];
297
 
              RELEASE(h);
298
 
            }
299
 
          [self postReadNotification];
300
 
        }
301
 
      else if (operation == NSFileHandleDataAvailableNotification)
302
 
        {
303
 
          [self postReadNotification];
304
 
        }
305
 
      else
306
 
        {
307
 
          NSMutableData *item;
308
 
          int           length;
309
 
          int           received = 0;
310
 
          char          buf[NETBUF_SIZE];
311
 
 
312
 
          item = [readInfo objectForKey: NSFileHandleNotificationDataItem];
313
 
          length = [item length];
314
 
 
315
 
          if (connected)
316
 
            {
317
 
              received = SSL_read(ssl, buf, sizeof(buf));
318
 
            }
319
 
          else
320
 
            {
321
 
              received = read(descriptor, buf, sizeof(buf));
322
 
            }
323
 
          if (received == 0)
324
 
            { // Read up to end of file.
325
 
              [self postReadNotification];
326
 
            }
327
 
          else if (received < 0)
328
 
            {
329
 
              if (errno != EAGAIN && errno != EINTR)
330
 
                {
331
 
                  NSString      *s;
332
 
 
333
 
                  s = [NSString stringWithFormat: @"Read attempt failed - %s",
334
 
                    GSLastErrorStr(errno)];
335
 
                  [readInfo setObject: s forKey: GSFileHandleNotificationError];
336
 
                  [self postReadNotification];
337
 
                }
338
 
            }
339
 
          else
340
 
            {
341
 
              [item appendBytes: buf length: received];
342
 
              if (operation == NSFileHandleReadCompletionNotification)
343
 
                {
344
 
                  // Read a single chunk of data
345
 
                  [self postReadNotification];
346
 
                }
347
 
            }
348
 
        }
349
 
    }
350
 
  else if (type == ET_WDESC)
351
 
    {
352
 
      NSMutableDictionary       *info;
353
 
 
354
 
      info = [writeInfo objectAtIndex: 0];
355
 
      operation = [info objectForKey: NotificationKey];
356
 
      if (operation == GSFileHandleWriteCompletionNotification)
357
 
        {
358
 
          NSData        *item;
359
 
          int           length;
360
 
          const void    *ptr;
361
 
 
362
 
          item = [info objectForKey: NSFileHandleNotificationDataItem];
363
 
          length = [item length];
364
 
          ptr = [item bytes];
365
 
          if (writePos < length)
366
 
            {
367
 
              int       written;
368
 
 
369
 
              if (connected)
370
 
                {
371
 
                  written = SSL_write(ssl, (char*)ptr + writePos, 
372
 
                    length - writePos);
373
 
                }
374
 
              else
375
 
                {
376
 
                  written = write(descriptor, (char*)ptr + writePos, 
377
 
                    length - writePos);
378
 
                }
379
 
              if (written <= 0)
380
 
                {
381
 
                  if (written < 0 && errno != EAGAIN && errno != EINTR)
382
 
                    {
383
 
                      NSString  *s;
384
 
 
385
 
                      s = [NSString stringWithFormat:
386
 
                        @"Write attempt failed - %s", GSLastErrorStr(errno)];
387
 
                      [info setObject: s forKey: GSFileHandleNotificationError];
388
 
                      [self postWriteNotification];
389
 
                    }
390
 
                }
391
 
              else
392
 
                {
393
 
                  writePos += written;
394
 
                }
395
 
            }
396
 
          if (writePos >= length)
397
 
            { // Write operation completed.
398
 
              [self postWriteNotification];
399
 
            }
400
 
        }
401
 
      else
402
 
        { // Connection attempt completed.
403
 
          int   result;
404
 
          int   len = sizeof(result);
405
 
 
406
 
          if (getsockopt(descriptor, SOL_SOCKET, SO_ERROR,
407
 
            (char*)&result, &len) == 0 && result != 0)
408
 
            {
409
 
                NSString        *s;
410
 
 
411
 
                s = [NSString stringWithFormat: @"Connect attempt failed - %s",
412
 
                  GSLastErrorStr(result)];
413
 
                [info setObject: s forKey: GSFileHandleNotificationError];
414
 
            }
415
 
          else
416
 
            {
417
 
              readOK = YES;
418
 
              writeOK = YES;
419
 
            }
420
 
          connectOK = NO;
421
 
          [self postWriteNotification];
422
 
        }
423
 
    }
424
 
}
425
 
 
426
 
- (BOOL) sslConnect
427
 
{
428
 
  int           ret;
429
 
  int           err;
430
 
  NSRunLoop     *loop;
431
 
 
432
 
  if (connected == YES)
433
 
    {
434
 
      return YES;       /* Already connected.   */
435
 
    }
436
 
  if (isStandardFile == YES)
437
 
    {
438
 
      NSLog(@"Attempt to make ssl connection to a standard file");
439
 
      return NO;
440
 
    }
441
 
 
442
 
  /*
443
 
   * Ensure we have a context and handle to connect with.
444
 
   */
445
 
  if (ctx == 0)
446
 
    {
447
 
      ctx = SSL_CTX_new(SSLv23_client_method());
448
 
    }
449
 
  if (ssl == 0)
450
 
    {
451
 
      ssl = SSL_new(ctx);
452
 
    }
453
 
 
454
 
  ret = SSL_set_fd(ssl, descriptor);
455
 
  loop = [NSRunLoop currentRunLoop];
456
 
  [loop runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
457
 
  ret = SSL_connect(ssl);
458
 
  if (ret != 1)
459
 
    {
460
 
      int               e = errno;
461
 
      NSDate            *final;
462
 
      NSDate            *when;
463
 
      NSTimeInterval    last = 0.0;
464
 
      NSTimeInterval    limit = 0.1;
465
 
 
466
 
      final = [[NSDate alloc] initWithTimeIntervalSinceNow: 20.0];
467
 
      when = [NSDate alloc];
468
 
 
469
 
      err = SSL_get_error(ssl, ret);
470
 
      while ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
471
 
        && [final timeIntervalSinceNow] > 0.0)
472
 
        {
473
 
          NSTimeInterval        tmp = limit;
474
 
 
475
 
          limit += last;
476
 
          last = tmp;
477
 
          when = [when initWithTimeIntervalSinceNow: limit];
478
 
          [loop runUntilDate: when];
479
 
          ret = SSL_connect(ssl);
480
 
          if (ret != 1)
481
 
            {
482
 
              e = errno;
483
 
              err = SSL_get_error(ssl, ret);
484
 
            }
485
 
          else
486
 
            {
487
 
              err = SSL_ERROR_NONE;
488
 
            }
489
 
        }
490
 
      RELEASE(when);
491
 
      RELEASE(final);
492
 
      if (err != SSL_ERROR_NONE)
493
 
        {
494
 
          NSString      *str;
495
 
 
496
 
          switch (err)
497
 
            {
498
 
              case SSL_ERROR_NONE:
499
 
                str = @"No error: really helpful";
500
 
                break;
501
 
              case SSL_ERROR_ZERO_RETURN:
502
 
                str = @"Zero Return error";
503
 
                break;
504
 
              case SSL_ERROR_WANT_READ:
505
 
                str = @"Want Read Error";
506
 
                break;
507
 
              case SSL_ERROR_WANT_WRITE:
508
 
                str = @"Want Write Error";
509
 
                break;
510
 
              case SSL_ERROR_WANT_X509_LOOKUP:
511
 
                str = @"Want X509 Lookup Error";
512
 
                break;
513
 
              case SSL_ERROR_SYSCALL:
514
 
                str = [NSString stringWithFormat: @"Syscall error %d - %s",
515
 
                  e, GSLastErrorStr(e)];
516
 
                break;
517
 
              case SSL_ERROR_SSL:
518
 
                str = @"SSL Error: really helpful";
519
 
                break;
520
 
              default:
521
 
                str = @"Standard Unix Error: really helpful";
522
 
                break;
523
 
            }
524
 
          NSLog(@"unable to make SSL connection to %@:%@ - %@",
525
 
            address, service, str);
526
 
          return NO;
527
 
        }
528
 
    }
529
 
  connected = YES;
530
 
  return YES;
531
 
}
532
 
 
533
 
- (void) sslDisconnect
534
 
{
535
 
  if (ssl != 0)
536
 
    {
537
 
      if (connected == YES)
538
 
        {
539
 
          SSL_shutdown(ssl);
540
 
        }
541
 
      SSL_clear(ssl);
542
 
      SSL_free(ssl);
543
 
      ssl = 0;
544
 
    }
545
 
  if (ctx != 0)
546
 
    {
547
 
      SSL_CTX_free(ctx);
548
 
      ctx = 0;
549
 
    }
550
 
  connected = NO;
551
 
}
552
 
 
553
 
- (void) sslSetCertificate: (NSString*)certFile
554
 
                privateKey: (NSString*)privateKey
555
 
                 PEMpasswd: (NSString*)PEMpasswd
556
 
{
557
 
  if (isStandardFile == YES)
558
 
    {
559
 
      NSLog(@"Attempt to set ssl certificate for a standard file");
560
 
      return;
561
 
    }
562
 
  /*
563
 
   * Ensure we have a context to set the certificate for.
564
 
   */
565
 
  if (ctx == 0)
566
 
    {
567
 
      ctx = SSL_CTX_new(SSLv23_client_method());
568
 
    }
569
 
  if ([PEMpasswd length] > 0)
570
 
    {
571
 
      SSL_CTX_set_default_passwd_cb_userdata(ctx, (char*)[PEMpasswd cString]);
572
 
    }
573
 
  if ([certFile length] > 0)
574
 
    {
575
 
      SSL_CTX_use_certificate_file(ctx, [certFile cString], X509_FILETYPE_PEM);
576
 
    }
577
 
  if ([privateKey length] > 0)
578
 
    {
579
 
      SSL_CTX_use_PrivateKey_file(ctx, [privateKey cString], X509_FILETYPE_PEM);
580
 
    }
581
 
}
582
 
 
583
 
- (void) writeData: (NSData*)item
584
 
{
585
 
  int           rval = 0;
586
 
  const void    *ptr = [item bytes];
587
 
  unsigned int  len = [item length];
588
 
  unsigned int  pos = 0;
589
 
 
590
 
  [self checkWrite];
591
 
  if (isNonBlocking == YES)
592
 
    {
593
 
      [self setNonBlocking: NO];
594
 
    }
595
 
  while (pos < len)
596
 
    {
597
 
      int       toWrite = len - pos;
598
 
 
599
 
      if (toWrite > NETBUF_SIZE)
600
 
        {
601
 
          toWrite = NETBUF_SIZE;
602
 
        }
603
 
      if (connected)
604
 
        {
605
 
          rval = SSL_write(ssl, (char*)ptr+pos, toWrite);
606
 
        }
607
 
      else
608
 
        {
609
 
          rval = write(descriptor, (char*)ptr+pos, toWrite);
610
 
        }
611
 
      if (rval < 0)
612
 
        {
613
 
          if (errno == EAGAIN == errno == EINTR)
614
 
            {
615
 
              rval = 0;
616
 
            }
617
 
          else
618
 
            {
619
 
              break;
620
 
            }
621
 
        }
622
 
      pos += rval;
623
 
    }
624
 
  if (rval < 0)
625
 
    {
626
 
      [NSException raise: NSFileHandleOperationException
627
 
                  format: @"unable to write to descriptor - %s",
628
 
                  GSLastErrorStr(errno)];
629
 
    }
630
 
}
631
 
@end
632