4aa955c108839ad3830f845c3f100226842d56f7
[external/uw-imap-toolkit.git] / imap-2007e / c-client / osdep.c
1 /* ========================================================================
2  * Copyright 1988-2007 University of Washington
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  * 
11  * ========================================================================
12  */
13
14 /*
15  * Program:     Operating-system dependent routines -- old Linux version
16  *
17  * Author:      Mark Crispin
18  *              Networks and Distributed Computing
19  *              Computing & Communications
20  *              University of Washington
21  *              Administration Building, AG-44
22  *              Seattle, WA  98195
23  *              Internet: MRC@CAC.Washington.EDU
24  *
25  * Date:        1 August 1993
26  * Last Edited: 16 August 2007
27  */
28
29 #include "tcp_unix.h"           /* must be before osdep includes tcp.h */
30 #include "mail.h"
31 #include "osdep.h"
32 #include <stdio.h>
33 #include <sys/time.h>
34 #include <sys/stat.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <netdb.h>
39 #include <ctype.h>
40 #include <errno.h>
41 extern int errno;               /* just in case */
42 #include <pwd.h>
43 #include "misc.h"
44
45
46 #include "fs_unix.c"
47 #include "ftl_unix.c"
48 #include "nl_unix.c"
49 #include "env_unix.c"
50 #define fork vfork
51 #include "tcp_unix.c"
52 #include "gr_waitp.c"
53 #include "tz_sv4.c"
54 #include "flocklnx.c"
55 /* ========================================================================
56  * Copyright 1988-2006 University of Washington
57  *
58  * Licensed under the Apache License, Version 2.0 (the "License");
59  * you may not use this file except in compliance with the License.
60  * You may obtain a copy of the License at
61  *
62  *     http://www.apache.org/licenses/LICENSE-2.0
63  *
64  * 
65  * ========================================================================
66  */
67
68 /*
69  * Program:     Standard check password
70  *
71  * Author:      Mark Crispin
72  *              Networks and Distributed Computing
73  *              Computing & Communications
74  *              University of Washington
75  *              Administration Building, AG-44
76  *              Seattle, WA  98195
77  *              Internet: MRC@CAC.Washington.EDU
78  *
79  * Date:        1 August 1988
80  * Last Edited: 30 August 2006
81  */
82 \f
83 /* Check password
84  * Accepts: login passwd struct
85  *          password string
86  *          argument count
87  *          argument vector
88  * Returns: passwd struct if password validated, NIL otherwise
89  */
90
91 struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
92 {
93   return (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
94 //       !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ?
95        !strcmp (pw->pw_passwd, pw->pw_passwd)) ?            
96         pw : NIL;
97 }
98 /* ========================================================================
99  * Copyright 1988-2006 University of Washington
100  *
101  * Licensed under the Apache License, Version 2.0 (the "License");
102  * you may not use this file except in compliance with the License.
103  * You may obtain a copy of the License at
104  *
105  *     http://www.apache.org/licenses/LICENSE-2.0
106  *
107  * 
108  * ========================================================================
109  */
110
111 /*
112  * Program:     Standard login
113  *
114  * Author:      Mark Crispin
115  *              Networks and Distributed Computing
116  *              Computing & Communications
117  *              University of Washington
118  *              Administration Building, AG-44
119  *              Seattle, WA  98195
120  *              Internet: MRC@CAC.Washington.EDU
121  *
122  * Date:        1 August 1988
123  * Last Edited: 30 August 2006
124  */
125 \f
126 /* Log in
127  * Accepts: login passwd struct
128  *          argument count
129  *          argument vector
130  * Returns: T if success, NIL otherwise
131  */
132
133 long loginpw (struct passwd *pw,int argc,char *argv[])
134 {
135   uid_t uid = pw->pw_uid;
136   char *name = cpystr (pw->pw_name);
137   long ret = !(setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) ||
138                setuid (uid));
139   fs_give ((void **) &name);
140   return ret;
141 }
142 /* ========================================================================
143  * Copyright 1988-2008 University of Washington
144  *
145  * Licensed under the Apache License, Version 2.0 (the "License");
146  * you may not use this file except in compliance with the License.
147  * You may obtain a copy of the License at
148  *
149  *     http://www.apache.org/licenses/LICENSE-2.0
150  *
151  * 
152  * ========================================================================
153  */
154
155 /*
156  * Program:     SSL authentication/encryption module
157  *
158  * Author:      Mark Crispin
159  *              Networks and Distributed Computing
160  *              Computing & Communications
161  *              University of Washington
162  *              Administration Building, AG-44
163  *              Seattle, WA  98195
164  *              Internet: MRC@CAC.Washington.EDU
165  *
166  * Date:        22 September 1998
167  * Last Edited: 13 January 2007
168  */
169 \f
170 #define crypt ssl_private_crypt
171 #include <x509v3.h>
172 #include <ssl.h>
173 #include <err.h>
174 #include <pem.h>
175 #include <buffer.h>
176 #include <bio.h>
177 #include <crypto.h>
178 #include <rand.h>
179 #undef crypt
180
181 #define SSLBUFLEN 8192
182 #define SSLCIPHERLIST "ALL:!LOW"
183
184
185 /* SSL I/O stream */
186
187 typedef struct ssl_stream {
188   TCPSTREAM *tcpstream;         /* TCP stream */
189   SSL_CTX *context;             /* SSL context */
190   SSL *con;                     /* SSL connection */
191   int ictr;                     /* input counter */
192   char *iptr;                   /* input pointer */
193   char ibuf[SSLBUFLEN];         /* input buffer */
194 } SSLSTREAM;
195
196 #include "sslio.h"
197 \f
198 /* Function prototypes */
199
200 static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
201 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
202 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
203 static char *ssl_validate_cert (X509 *cert,char *host);
204 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
205 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
206                                long *contd);
207 static long ssl_abort (SSLSTREAM *stream);
208 static RSA *ssl_genkey (SSL *con,int export,int keylength);
209
210
211 /* Secure Sockets Layer network driver dispatch */
212
213 static struct ssl_driver ssldriver = {
214   ssl_open,                     /* open connection */
215   ssl_aopen,                    /* open preauthenticated connection */
216   ssl_getline,                  /* get a line */
217   ssl_getbuffer,                /* get a buffer */
218   ssl_soutr,                    /* output pushed data */
219   ssl_sout,                     /* output string */
220   ssl_close,                    /* close connection */
221   ssl_host,                     /* return host name */
222   ssl_remotehost,               /* return remote host name */
223   ssl_port,                     /* return port number */
224   ssl_localhost                 /* return local host name */
225 };
226                                 /* non-NIL if doing SSL primary I/O */
227 static SSLSTDIOSTREAM *sslstdio = NIL;
228 static char *start_tls = NIL;   /* non-NIL if start TLS requested */
229 \f
230 /* One-time SSL initialization */
231
232 static int sslonceonly = 0;
233
234 void ssl_onceonlyinit (void)
235 {
236   if (!sslonceonly++) {         /* only need to call it once */
237     int fd;
238     char tmp[MAILTMPLEN];
239     struct stat sbuf;
240                                 /* if system doesn't have /dev/urandom */
241     if (stat ("/dev/urandom",&sbuf)) {
242       while ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT|O_EXCL,0600)) < 0)
243         sleep (1);
244       unlink (tmp);             /* don't need the file */
245       fstat (fd,&sbuf);         /* get information about the file */
246       close (fd);               /* flush descriptor */
247                                 /* not great but it'll have to do */
248       sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
249                tcp_serveraddr (),(unsigned long) tcp_serverport (),
250                tcp_clientaddr (),(unsigned long) tcp_clientport (),
251                (unsigned long) sbuf.st_ino,(unsigned long) time (0),
252                (unsigned long) gethostid (),(unsigned long) getpid ());
253       RAND_seed (tmp,strlen (tmp));
254     }
255                                 /* apply runtime linkage */
256     mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
257     mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
258     SSL_library_init ();        /* add all algorithms */
259   }
260 }
261 \f
262 /* SSL open
263  * Accepts: host name
264  *          contact service name
265  *          contact port number
266  * Returns: SSL stream if success else NIL
267  */
268
269 SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
270 {
271   TCPSTREAM *stream = tcp_open (host,service,port);
272   return stream ? ssl_start (stream,host,port) : NIL;
273 }
274
275
276 /* SSL authenticated open
277  * Accepts: host name
278  *          service name
279  *          returned user name buffer
280  * Returns: SSL stream if success else NIL
281  */
282
283 SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
284 {
285   return NIL;                   /* don't use this mechanism with SSL */
286 }
287 \f
288 /* Start SSL/TLS negotiations
289  * Accepts: open TCP stream of session
290  *          user's host name
291  *          flags
292  * Returns: SSL stream if success else NIL
293  */
294
295 static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
296 {
297   char *reason,tmp[MAILTMPLEN];
298   sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
299   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
300   void *data = (*bn) (BLOCK_SENSITIVE,NIL);
301   SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
302                                             sizeof (SSLSTREAM));
303   stream->tcpstream = tstream;  /* bind TCP stream */
304                                 /* do the work */
305   reason = ssl_start_work (stream,host,flags);
306   (*bn) (BLOCK_NONSENSITIVE,data);
307   if (reason) {                 /* failed? */
308     ssl_close (stream);         /* failed to do SSL */
309     stream = NIL;               /* no stream returned */
310     switch (*reason) {          /* analyze reason */
311     case '*':                   /* certificate failure */
312       ++reason;                 /* skip over certificate failure indication */
313                                 /* pass to error callback */
314       if (sf) (*sf) (host,reason,flags);
315       else {                    /* no error callback, build error message */
316         sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
317         mm_log (tmp,ERROR);
318       }
319     case '\0':                  /* user answered no to certificate callback */
320       if (flags & NET_TRYSSL)   /* return dummy stream to stop tryssl */
321         stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
322                                        sizeof (SSLSTREAM));
323       break;
324     default:                    /* non-certificate failure */
325       if (flags & NET_TRYSSL);  /* no error output if tryssl */
326                                 /* pass to error callback */
327       else if (sf) (*sf) (host,reason,flags);
328       else {                    /* no error callback, build error message */
329         sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
330         mm_log (tmp,ERROR);
331       }
332       break;
333     }
334   }
335   return stream;
336 }
337 \f
338 /* Start SSL/TLS negotiations worker routine
339  * Accepts: SSL stream
340  *          user's host name
341  *          flags
342  * Returns: NIL if success, else error reason
343  */
344
345                                 /* evil but I had no choice */
346 static char *ssl_last_error = NIL;
347 static char *ssl_last_host = NIL;
348
349 /* #define __NON_BLOCKING_SSL_WRITE__ */
350
351 #ifdef __NON_BLOCKING_SSL_WRITE__
352 /*  g.shyamakshi@samsung.com 
353  * is_socket_ready() returns T if socket is ready, else returns NIL for timeout 
354  */
355 static int is_socket_ready ( int sock_fd, int mode) // 0 - read; 1 - write, 2 - connect
356 {
357         fd_set rset, wset;
358         struct timeval tv = {10,0};
359         int rc = T;
360
361         if (sock_fd < 0)
362                 return -1;
363
364         FD_ZERO(&rset);
365         FD_SET(sock_fd, &rset);
366         wset = rset;
367         
368         /* Wait for 10 seconds to check if the socket is ready */
369         switch (mode)
370         {
371                 case 0: /* Check if read socket is ready */
372                         rc = select(sock_fd+1, &rset, NULL, NULL, &tv);
373                         break;
374                 case 1: /* Check if write socket is ready */
375                         rc = select(sock_fd+1, NULL, &wset, NULL, &tv);
376                         break;
377                 case 2: /* Check if both read and write socket is ready */
378                         rc = select(sock_fd+1, &rset, &wset, NULL, &tv);
379                         break;
380         }
381         
382         return rc;
383 }
384 #endif
385 static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
386 {
387   BIO *bio;
388   X509 *cert;
389   unsigned long sl,tl;
390   char *s,*t,*err,tmp[MAILTMPLEN];
391   sslcertificatequery_t scq =
392     (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
393   sslclientcert_t scc =
394     (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL);
395   sslclientkey_t sck =
396     (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL);
397   if (ssl_last_error) fs_give ((void **) &ssl_last_error);
398   ssl_last_host = host;
399   if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ?
400                                        TLSv1_client_method () :
401                                        SSLv23_client_method ())))
402     return "SSL context failed";
403   SSL_CTX_set_options (stream->context,0);
404                                 /* disable certificate validation? */
405   if (flags & NET_NOVALIDATECERT)
406     SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
407   else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify);
408                                 /* set default paths to CAs... */
409   SSL_CTX_set_default_verify_paths (stream->context);
410                                 /* ...unless a non-standard path desired */
411   if (s = (char *) mail_parameters (NIL,GET_SSLCAPATH,NIL))
412     SSL_CTX_load_verify_locations (stream->context,NIL,s);
413                                 /* want to send client certificate? */
414   if (scc && (s = (*scc) ()) && (sl = strlen (s))) {
415     if (cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) {
416       SSL_CTX_use_certificate (stream->context,cert);
417       X509_free (cert);
418     }
419     BIO_free (bio);
420     if (!cert) return "SSL client certificate failed";
421                                 /* want to supply private key? */
422     if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
423       EVP_PKEY *key;
424       if (key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl),
425                                          NIL,NIL,"")) {
426         SSL_CTX_use_PrivateKey (stream->context,key);
427         EVP_PKEY_free (key);
428       }
429       BIO_free (bio);
430       memset (t,0,tl);          /* erase key */
431     }
432     if (s != t) memset (s,0,sl);/* erase certificate if different from key */
433   }
434 \f
435                                 /* create connection */
436   if (!(stream->con = (SSL *) SSL_new (stream->context)))
437     return "SSL connection failed";
438   bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
439   SSL_set_bio (stream->con,bio,bio);
440   SSL_set_connect_state (stream->con);
441   if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
442                                 /* now negotiate SSL */
443   if (SSL_write (stream->con,"",0) < 0)
444     return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
445                                 /* need to validate host names? */
446   if (!(flags & NET_NOVALIDATECERT) &&
447       (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
448                                 host))) {
449                                 /* application callback */
450     if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : "";
451                                 /* error message to return via mm_log() */
452     sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
453     return ssl_last_error = cpystr (tmp);
454   }
455   return NIL;
456 }
457 \f
458 /* SSL certificate verification callback
459  * Accepts: error flag
460  *          X509 context
461  * Returns: error flag
462  */
463
464 static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
465 {
466   char *err,cert[256],tmp[MAILTMPLEN];
467   sslcertificatequery_t scq =
468     (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
469   if (!ok) {                    /* in case failure */
470     err = (char *) X509_verify_cert_error_string
471       (X509_STORE_CTX_get_error (ctx));
472     X509_NAME_oneline (X509_get_subject_name
473                        (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
474     if (!scq) {                 /* mm_log() error message if no callback */
475       sprintf (tmp,"*%.128s: %.255s",err,cert);
476       ssl_last_error = cpystr (tmp);
477     }
478                                 /* ignore error if application says to */
479     else if ((*scq) (err,ssl_last_host,cert)) ok = T;
480                                 /* application wants punt */
481     else ssl_last_error = cpystr ("");
482   }
483   return ok;
484 }
485
486
487 /* SSL validate certificate
488  * Accepts: certificate
489  *          host to validate against
490  * Returns: NIL if validated, else string of error message
491  */
492
493 static char *ssl_validate_cert (X509 *cert,char *host)
494 {
495   int i,n;
496   char *s,*t,*ret;
497   void *ext;
498   GENERAL_NAME *name;
499                                 /* make sure have a certificate */
500   if (!cert) ret = "No certificate from server";
501                                 /* and that it has a name */
502   else if (!cert->name) ret = "No name in certificate";
503                                 /* locate CN */
504   else if (s = strstr (cert->name,"/CN=")) {
505     if (t = strchr (s += 4,'/')) *t = '\0';
506                                 /* host name matches pattern? */
507     ret = ssl_compare_hostnames (host,s) ? NIL :
508       "Server name does not match certificate";
509     if (t) *t = '/';            /* restore smashed delimiter */
510                                 /* if mismatch, see if in extensions */
511     if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
512         (n = sk_GENERAL_NAME_num (ext)))
513       /* older versions of OpenSSL use "ia5" instead of dNSName */
514       for (i = 0; ret && (i < n); i++)
515         if ((name = sk_GENERAL_NAME_value (ext,i)) &&
516             (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
517             ssl_compare_hostnames (host,s)) ret = NIL;
518   }
519   else ret = "Unable to locate common name in certificate";
520   return ret;
521 }
522 \f
523 /* Case-independent wildcard pattern match
524  * Accepts: base string
525  *          pattern string
526  * Returns: T if pattern matches base, else NIL
527  */
528
529 static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
530 {
531   long ret = NIL;
532   switch (*pat) {
533   case '*':                     /* wildcard */
534     if (pat[1]) {               /* there must be a pattern suffix */
535                                 /* there is, scan base against it */
536       do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
537       while (!ret && (*s != '.') && *s++);
538     }
539     break;
540   case '\0':                    /* end of pattern */
541     if (!*s) ret = LONGT;       /* success if base is also at end */
542     break;
543   default:                      /* non-wildcard, recurse if match */
544     if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
545     break;
546   }
547   return ret;
548 }
549 \f
550 /* SSL receive line
551  * Accepts: SSL stream
552  * Returns: text line string or NIL if failure
553  */
554
555 char *ssl_getline (SSLSTREAM *stream)
556 {
557   unsigned long n,contd;
558   char *ret = ssl_getline_work (stream,&n,&contd);
559   if (ret && contd) {           /* got a line needing continuation? */
560     STRINGLIST *stl = mail_newstringlist ();
561     STRINGLIST *stc = stl;
562     do {                        /* collect additional lines */
563       stc->text.data = (unsigned char *) ret;
564       stc->text.size = n;
565       stc = stc->next = mail_newstringlist ();
566       ret = ssl_getline_work (stream,&n,&contd);
567     } while (ret && contd);
568     if (ret) {                  /* stash final part of line on list */
569       stc->text.data = (unsigned char *) ret;
570       stc->text.size = n;
571                                 /* determine how large a buffer we need */
572       for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
573       ret = fs_get (n + 1);     /* copy parts into buffer */
574       for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
575         memcpy (ret + n,stc->text.data,stc->text.size);
576       ret[n] = '\0';
577     }
578     mail_free_stringlist (&stl);/* either way, done with list */
579   }
580   return ret;
581 }
582 \f
583 /* SSL receive line or partial line
584  * Accepts: SSL stream
585  *          pointer to return size
586  *          pointer to return continuation flag
587  * Returns: text line string, size and continuation flag, or NIL if failure
588  */
589
590 static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
591                                long *contd)
592 {
593   unsigned long n;
594   char *s,*ret,c,d;
595   *contd = NIL;                 /* assume no continuation */
596                                 /* make sure have data */
597   if (!ssl_getdata (stream)) return NIL;
598   for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
599     d = *stream->iptr++;        /* slurp another character */
600     if ((c == '\015') && (d == '\012')) {
601       ret = (char *) fs_get (n--);
602       memcpy (ret,s,*size = n); /* copy into a free storage string */
603       ret[n] = '\0';            /* tie off string with null */
604       return ret;
605     }
606   }
607                                 /* copy partial string from buffer */
608   memcpy ((ret = (char *) fs_get (n)),s,*size = n);
609                                 /* get more data from the net */
610   if (!ssl_getdata (stream)) fs_give ((void **) &ret);
611                                 /* special case of newline broken by buffer */
612   else if ((c == '\015') && (*stream->iptr == '\012')) {
613     stream->iptr++;             /* eat the line feed */
614     stream->ictr--;
615     ret[*size = --n] = '\0';    /* tie off string with null */
616   }
617   else *contd = LONGT;          /* continuation needed */
618   return ret;
619 }
620 \f
621 /* SSL receive buffer
622  * Accepts: SSL stream
623  *          size in bytes
624  *          buffer to read into
625  * Returns: T if success, NIL otherwise
626  */
627
628 long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
629 {
630   unsigned long n;
631   while (size > 0) {            /* until request satisfied */
632     if (!ssl_getdata (stream)) return NIL;
633     n = min (size,stream->ictr);/* number of bytes to transfer */
634                                 /* do the copy */
635     memcpy (buffer,stream->iptr,n);
636     buffer += n;                /* update pointer */
637     stream->iptr += n;
638     size -= n;                  /* update # of bytes to do */
639     stream->ictr -= n;
640   }
641   buffer[0] = '\0';             /* tie off string */
642   return T;
643 }
644 \f
645 /* SSL receive data
646  * Accepts: TCP/IP stream
647  * Returns: T if success, NIL otherwise
648  */
649
650 long ssl_getdata (SSLSTREAM *stream)
651 {
652   int i,sock;
653   fd_set fds,efds;
654   struct timeval tmo;
655   tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
656   long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
657   time_t t = time (0);
658   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
659   if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
660                                 /* tcp_unix should have prevented this */
661   if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
662   (*bn) (BLOCK_TCPREAD,NIL);
663   while (stream->ictr < 1) {    /* if nothing in the buffer */
664     time_t tl = time (0);       /* start of request */
665     time_t now = tl;
666     int ti = ttmo_read ? now + ttmo_read : 0;
667     if (SSL_pending (stream->con)) i = 1;
668     else {
669       if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
670       tmo.tv_usec = 0;
671       FD_ZERO (&fds);           /* initialize selection vector */
672       FD_ZERO (&efds);          /* handle errors too */
673       FD_SET (sock,&fds);       /* set bit in selection vector */
674       FD_SET (sock,&efds);      /* set bit in error selection vector */
675       errno = NIL;              /* block and read */
676       do {                      /* block under timeout */
677         tmo.tv_sec = ti ? ti - now : 0;
678         i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
679         now = time (0);         /* fake timeout if interrupt & time expired */
680         if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
681       } while ((i < 0) && (errno == EINTR));
682     }
683     if (i) {                    /* non-timeout result from select? */
684       errno = 0;                /* just in case */
685       if (i > 0)                /* read what we can */
686         while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
687                ((errno == EINTR) ||
688                 (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
689       if (i <= 0) {             /* error seen? */
690         if (tcpdebug) {
691           char *s,tmp[MAILTMPLEN];
692           if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
693                           errno,SSL_get_error (stream->con,i));
694           else s = "SSL data read end of file";
695           mm_log (s,TCPDEBUG);
696         }
697         return ssl_abort (stream);
698       }
699       stream->iptr = stream->ibuf;/* point at TCP buffer */
700       stream->ictr = i;         /* set new byte count */
701       if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
702     }
703                                 /* timeout, punt unless told not to */
704     else if (!tmoh || !(*tmoh) (now - t,now - tl)) {
705       if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
706       return ssl_abort (stream);
707     }
708   }
709   (*bn) (BLOCK_NONE,NIL);
710   return T;
711 }
712 \f
713 /* SSL send string as record
714  * Accepts: SSL stream
715  *          string pointer
716  * Returns: T if success else NIL
717  */
718
719 long ssl_soutr (SSLSTREAM *stream,char *string)
720 {
721   return ssl_sout (stream,string,(unsigned long) strlen (string));
722 }
723
724
725 /* SSL send string
726  * Accepts: SSL stream
727  *          string pointer
728  *          byte count
729  * Returns: T if success else NIL
730  */
731
732 long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
733 {
734   long i;
735   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
736   if (!stream->con) return NIL;
737   
738 #ifdef __NON_BLOCKING_SSL_WRITE__       
739   (*bn) (BLOCK_NONE,NIL);
740   if (tcpdebug) mm_log("Writing to SSL",TCPDEBUG);
741 #else // __NON_BLOCKING_SSL_WRITE__
742   (*bn) (BLOCK_TCPWRITE,NIL);
743   if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
744 #endif // __NON_BLOCKING_SSL_WRITE__
745                                 /* until request satisfied */
746
747 #ifdef __NON_BLOCKING_SSL_WRITE__
748   
749   int sock = -1,flgs=0, ret = 0, err_code = -1, count = 1, r_count = 1;
750   char tmp[MAILTMPLEN]= {0,};
751   if ((sock = SSL_get_fd (stream->con)) < 0) return NIL;
752   
753   sprintf(tmp, "Socket_ID >>> %d", sock);
754   if (tcpdebug) mm_log(tmp, TCPDEBUG);
755
756   /* g.shyamakshi@samsung.com - Socket write non-blocking 
757      * wait for 50 seconds, return error if socket not ready to write
758      */
759
760   /* Set socket as non blocking */                              
761   flgs = fcntl (sock,F_GETFL,0);
762   fcntl (sock,F_SETFL,flgs | FNDELAY);
763
764
765   for (i = 0; size > 0; string += i,size -= i)
766   {
767                                 /* write as much as we can */
768     
769         sprintf(tmp, "Attempting... size %d ", size );
770         if (tcpdebug) mm_log (tmp,TCPDEBUG);
771     count = 1;
772         r_count = 1;
773         do
774         {
775                 err_code = -1;
776             if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) <= 0) 
777                 {
778                         err_code = SSL_get_error (stream->con,i);
779                         sprintf(tmp, "SSL write failed with return value - %d and error code %d", i, err_code );
780                         mm_log(tmp, WARN);      
781                         i = 0;
782                         switch(err_code)
783                         {
784                                 case SSL_ERROR_WANT_READ:
785                                         {
786                                                 // mm_log("Inside SSL_ERROR_WANT_READ", WARN);
787                                                 ret = is_socket_ready(sock, 0);
788                                                 if( !ret )
789                                                 {
790                                                         // mm_log("Socket not ready... Attempt write again... ", WARN);
791                                                         if ( count >= 10 )
792                                                         {
793                                                                 mm_log("Timeout.. SSL write failed", ERROR);
794                                                                 return ssl_abort (stream);/* write failed */
795                                                         }
796                                                         else
797                                                         {
798                                                                 count++;
799                                                         }
800                                                 }
801                                                 else if( ret > 0 )
802                                                 {
803                                                         // mm_log("Socket now ready... Attempt again ...", WARN);
804                                                         err_code = -1;
805                                                         if ( r_count >= 10 )
806                                                         {
807                                                                 mm_log(" Tried multiple times.. but SSL write failed", ERROR);
808                                                                 return ssl_abort (stream);/* write failed */
809                                                         }
810                                                         else
811                                                         {
812                                                                 r_count++;
813                                                         }                                                       
814                                                 }
815                                                 else
816                                                 {
817                                                         mm_log(" Invalid Sock ID ", ERROR);
818                                                         return ssl_abort (stream);/* write failed */
819                                                 }
820                                         }
821                                         break;
822                                 case SSL_ERROR_WANT_WRITE:
823                                         {
824                                                 // mm_log("Inside SSL_ERROR_WANT_WRITE", WARN);
825                                                 ret = is_socket_ready(sock, 1);
826                                                 if( !ret )
827                                                 {
828                                                         // mm_log("Attempt write again... ", WARN);
829                                                         if ( count >= 10 )
830                                                         {
831                                                                 mm_log("Timeout.. SSL write failed", ERROR);
832                                                                 return ssl_abort (stream);/* write failed */
833                                                         }
834                                                         else
835                                                         {
836                                                                 count++;
837                                                         }
838                                                 }
839                                                 else if( ret > 0 )
840                                                 {
841                                                         // mm_log("Socket now ready... Attempt again ...", WARN);
842                                                         err_code = -1;
843                                                         if ( r_count >= 10 )
844                                                         {
845                                                                 mm_log(" Tried multiple times.. but SSL write failed", ERROR);
846                                                                 return ssl_abort (stream);/* write failed */
847                                                         }
848                                                         else
849                                                         {
850                                                                 r_count++;
851                                                         }                                                       
852                                                 }
853                                                 else
854                                                 {
855                                                         mm_log(" Invalid Sock ID ", ERROR);
856                                                         return ssl_abort (stream);/* write failed */
857                                                 }
858
859                                         }
860                                         break;
861                                 default:
862                                         {
863                                                 mm_log(" SSL write aborting ", ERROR);
864                                                 return ssl_abort (stream);/* write failed */    
865                                         }
866                                         break;
867                         }
868             }
869         }while(err_code == SSL_ERROR_WANT_READ || err_code == SSL_ERROR_WANT_WRITE );
870   }
871
872   /* Set socket as blocking again */                            
873   fcntl (sock,F_SETFL,flgs);
874 #else // __NON_BLOCKING_SSL_WRITE__
875   for (i = 0; size > 0; string += i,size -= i) /* write as much as we can */
876     if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
877       if (tcpdebug) {
878         char tmp[MAILTMPLEN];
879         sprintf (tmp,"SSL data write I/O error %d SSL error %d",
880           errno,SSL_get_error (stream->con,i));
881         mm_log (tmp,TCPDEBUG);
882       }
883       return ssl_abort (stream);/* write failed */
884     }
885 #endif // __NON_BLOCKING_SSL_WRITE__
886   
887   if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
888   (*bn) (BLOCK_NONE,NIL);
889   return LONGT;                 /* all done */
890 }
891 \f
892 /* SSL close
893  * Accepts: SSL stream
894  */
895
896 void ssl_close (SSLSTREAM *stream)
897 {
898   ssl_abort (stream);           /* nuke the stream */
899   fs_give ((void **) &stream);  /* flush the stream */
900 }
901
902
903 /* SSL abort stream
904  * Accepts: SSL stream
905  * Returns: NIL always
906  */
907
908 static long ssl_abort (SSLSTREAM *stream)
909 {
910   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
911   if (stream->con) {            /* close SSL connection */
912     SSL_shutdown (stream->con);
913     SSL_free (stream->con);
914     stream->con = NIL;
915   }
916   if (stream->context) {        /* clean up context */
917     SSL_CTX_free (stream->context);
918     stream->context = NIL;
919   }
920   if (stream->tcpstream) {      /* close TCP stream */
921     tcp_close (stream->tcpstream);
922     stream->tcpstream = NIL;
923   }
924   (*bn) (BLOCK_NONE,NIL);
925   return NIL;
926 }
927 \f
928 /* SSL get host name
929  * Accepts: SSL stream
930  * Returns: host name for this stream
931  */
932
933 char *ssl_host (SSLSTREAM *stream)
934 {
935   return tcp_host (stream->tcpstream);
936 }
937
938
939 /* SSL get remote host name
940  * Accepts: SSL stream
941  * Returns: host name for this stream
942  */
943
944 char *ssl_remotehost (SSLSTREAM *stream)
945 {
946   return tcp_remotehost (stream->tcpstream);
947 }
948
949
950 /* SSL return port for this stream
951  * Accepts: SSL stream
952  * Returns: port number for this stream
953  */
954
955 unsigned long ssl_port (SSLSTREAM *stream)
956 {
957   return tcp_port (stream->tcpstream);
958 }
959
960
961 /* SSL get local host name
962  * Accepts: SSL stream
963  * Returns: local host name
964  */
965
966 char *ssl_localhost (SSLSTREAM *stream)
967 {
968   return tcp_localhost (stream->tcpstream);
969 }
970 \f
971 /* Start TLS
972  * Accepts: /etc/services service name
973  * Returns: cpystr'd error string if TLS failed, else NIL for success
974  */
975
976 char *ssl_start_tls (char *server)
977 {
978   char tmp[MAILTMPLEN];
979   struct stat sbuf;
980   if (sslstdio) return cpystr ("Already in an SSL session");
981   if (start_tls) return cpystr ("TLS already started");
982   if (server) {                 /* build specific certificate/key file name */
983     sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
984     if (stat (tmp,&sbuf)) {     /* use non-specific name if no specific file */
985       sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
986       if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
987     }
988     start_tls = server;         /* switch to STARTTLS mode */
989   }
990   return NIL;
991 }
992 \f
993 /* Init server for SSL
994  * Accepts: server name
995  */
996
997 void ssl_server_init (char *server)
998 {
999   char cert[MAILTMPLEN],key[MAILTMPLEN];
1000   unsigned long i;
1001   struct stat sbuf;
1002   SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
1003                                             sizeof (SSLSTREAM));
1004   ssl_onceonlyinit ();          /* make sure algorithms added */
1005   ERR_load_crypto_strings ();
1006   SSL_load_error_strings ();
1007                                 /* build specific certificate/key file names */
1008   sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
1009   sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
1010                                 /* use non-specific name if no specific cert */
1011   if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
1012   if (stat (key,&sbuf)) {       /* use non-specific name if no specific key */
1013     sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server);
1014                                 /* use cert file as fallback for key */
1015     if (stat (key,&sbuf)) strcpy (key,cert);
1016   }
1017                                 /* create context */
1018   if (!(stream->context = SSL_CTX_new (start_tls ?
1019                                        TLSv1_server_method () :
1020                                        SSLv23_server_method ())))
1021     syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
1022             tcp_clienthost ());
1023   else {                        /* set context options */
1024     SSL_CTX_set_options (stream->context,SSL_OP_ALL);
1025                                 /* set cipher list */
1026     if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
1027       syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
1028               SSLCIPHERLIST,tcp_clienthost ());
1029                                 /* load certificate */
1030     else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
1031       syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
1032               cert,tcp_clienthost ());
1033                                 /* load key */
1034     else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
1035                                                SSL_FILETYPE_PEM)))
1036       syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
1037               key,tcp_clienthost ());
1038 \f
1039     else {                      /* generate key if needed */
1040       if (SSL_CTX_need_tmp_RSA (stream->context))
1041         SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
1042                                 /* create new SSL connection */
1043       if (!(stream->con = SSL_new (stream->context)))
1044         syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
1045                 tcp_clienthost ());
1046       else {                    /* set file descriptor */
1047         SSL_set_fd (stream->con,0);
1048                                 /* all OK if accepted */
1049         if (SSL_accept (stream->con) < 0)
1050           syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
1051                   tcp_clienthost ());
1052         else {                  /* server set up */
1053           sslstdio = (SSLSTDIOSTREAM *)
1054             memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
1055           sslstdio->sslstream = stream;
1056                                 /* available space in output buffer */
1057           sslstdio->octr = SSLBUFLEN;
1058                                 /* current output buffer pointer */
1059           sslstdio->optr = sslstdio->obuf;
1060                                 /* allow plaintext if disable value was 2 */
1061           if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
1062             mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
1063                                 /* unhide PLAIN SASL authenticator */
1064           mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
1065           mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
1066           return;
1067         }
1068       }
1069     }  
1070   }
1071   while (i = ERR_get_error ())  /* SSL failure */
1072     syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
1073   ssl_close (stream);           /* punt stream */
1074   exit (1);                     /* punt this program too */
1075 }
1076 \f
1077 /* Generate one-time key for server
1078  * Accepts: SSL connection
1079  *          export flag
1080  *          keylength
1081  * Returns: generated key, always
1082  */
1083
1084 static RSA *ssl_genkey (SSL *con,int export,int keylength)
1085 {
1086   unsigned long i;
1087   static RSA *key = NIL;
1088   if (!key) {                   /* if don't have a key already */
1089                                 /* generate key */
1090     if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
1091       syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
1092               tcp_clienthost ());
1093       while (i = ERR_get_error ())
1094         syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
1095       exit (1);
1096     }
1097   }
1098   return key;
1099 }
1100 \f
1101 /* Wait for stdin input
1102  * Accepts: timeout in seconds
1103  * Returns: T if have input on stdin, else NIL
1104  */
1105
1106 long ssl_server_input_wait (long seconds)
1107 {
1108   int i,sock;
1109   fd_set fds,efd;
1110   struct timeval tmo;
1111   SSLSTREAM *stream;
1112   if (!sslstdio) return server_input_wait (seconds);
1113                                 /* input available in buffer */
1114   if (((stream = sslstdio->sslstream)->ictr > 0) ||
1115       !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
1116                                 /* sock ought to be 0 always */
1117   if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
1118                                 /* input available from SSL */
1119   if (SSL_pending (stream->con) &&
1120       ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
1121     stream->iptr = stream->ibuf;/* point at TCP buffer */
1122     stream->ictr = i;           /* set new byte count */
1123     return LONGT;
1124   }
1125   FD_ZERO (&fds);               /* initialize selection vector */
1126   FD_ZERO (&efd);               /* initialize selection vector */
1127   FD_SET (sock,&fds);           /* set bit in selection vector */
1128   FD_SET (sock,&efd);           /* set bit in selection vector */
1129   tmo.tv_sec = seconds; tmo.tv_usec = 0;
1130                                 /* see if input available from the socket */
1131   return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
1132 }
1133
1134 #include "sslstdio.c"