File: TLS.k

package info (click to toggle)
kaya 0.4.4-6.2
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 5,200 kB
  • ctags: 2,015
  • sloc: cpp: 9,556; haskell: 7,253; sh: 3,060; yacc: 910; makefile: 816; perl: 90
file content (232 lines) | stat: -rw-r--r-- 9,830 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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
224
225
226
227
228
229
230
231
232
/** -*-C-*-ish
    Kaya standard library
    Copyright (C) 2006 Chris Morris

    This file is distributed under the terms of the GNU Lesser General
    Public Licence. See COPYING for licence.
*/
"<summary>TLS network connection encryption</summary>
<prose>This module provides TLS network encryption. The functions in this module are called by the <moduleref>Net</moduleref> module and cannot usefully be used directly.</prose>
<prose>TLS encryption is only useful if the identity of the server connected to can be verified. The various networking functions in the <moduleref>Net</moduleref> and <moduleref>HTTP</moduleref> modules allow a list of certificate files to be specified. On POSIX systems, Certification Authority certificates are often stored in the <code>/etc/ssl/certs</code> directory, and if the signer of the server certificate is not known in advance, you may wish to specify many or all of the <code>.pem</code> files in this directory that correspond to CAs that you trust.</prose>"
module TLS;

import Prelude;
import Binary;

%include "tls_glue.h";
%include "gnutls/gnutls.h";
%imported "tls_glue";
%link "gnutls";
%link "gcrypt"; // needed for minGW, harmless on POSIX

"<summary>A TLS connection session</summary>
<prose>A TLS connection session</prose>"
abstract data TLSsession(Ptr sess, Ptr conn, Ptr cred);

"<argument>The argument is an error code from GNU TLS</argument> 
<summary>TLS handshake failed</summary>
<prose>This Exception will be thrown if the TLS connection cannot be established.</prose>"
Exception TLSFailed(Int err);

"<summary>TLS timeout</summary>
<prose>This Exception will be thrown if the TLS connection times out.</prose>"
Exception TLSTimeout;


"<argument>The argument is a description of the error</argument> 
<summary>TLS peer verification failed</summary>
<prose>This Exception will be thrown if verification of the TLS connection in <functionref>verifyCertificate</functionref> fails.</prose>"
Exception VerifyFailed(String err);

"<summary>TLS certificate acceptance failed</summary>
<prose>This Exception will be thrown if a TLS certificate file was invalid.</prose>"
Exception AcceptCertificateFailed;


foreign "tls_glue.o" {
  Void global_init_TLS() = gnutls_global_init;
  Ptr init_TLS(Ptr cred) = do_gnutls_init;
  Ptr initcred_TLS() = do_gnutls_makecred;
  Int assign_TLS(Ptr tls, Ptr conn) = do_gnutls_transport;
  Void put_TLS(Ptr tls, String toput) = do_gnutls_put;
  Void put_TLSbin(Ptr tls, Ptr toput, Int len) = do_gnutls_putbin;
  Void put_TLSbyte(Ptr tls, Int toput) = do_gnutls_putbyte;
  String get_TLS(Ptr tls, Ptr vm, Int len) = do_gnutls_get;
  Int get_TLSByte(Ptr tls, Ptr vm) = do_gnutls_getbyte;
  Ptr get_TLSBytes(Ptr tls, Ptr vm, a len) = do_gnutls_getbin;
  Void close_TLS(Ptr tls, Ptr cred) = do_gnutls_close;
  Void do_addcert(Ptr vm, Ptr cred, String fn) = do_addcert;
  Void do_verifycert(Ptr vm, Ptr sess, String host) = do_verifycert;
}

"<argument name='conn'>A pointer to the network connection resource</argument>
<summary>Initialise a TLS connection</summary>
<prose>Initialise a TLS connection. Called by <functionref>Net::connect</functionref> when the <code>usetls</code> parameter is true.</prose>
<related><functionref>acceptCertificate</functionref></related>
<related><functionref>verifyCertificate</functionref></related>"
public TLSsession makeTLS(Ptr conn) {
  global_init_TLS();
  cred = initcred_TLS();
  tls = init_TLS(cred);
  err = assign_TLS(tls,conn);
  if (err < 0) {
    throw(TLSFailed(err));
  }
  return TLSsession(tls,conn,cred);
}

"<argument name='session'>An open TLS Session</argument>
<argument name='certfile'>A file containing one or more PEM-encoded Certification Authority certificates.</argument>
<summary>Accept certificates.</summary>
<prose>Accept the certificates in the given file as signing certificates for the current connection.</prose>
<related><functionref>verifyCertificate</functionref></related>"
public Void acceptCertificate(TLSsession session, String certfile) {
  try {
    do_addcert(getVM(),session.cred,certfile);
  } catch(e) {
    throw(AcceptCertificateFailed);
  }
}

"<argument name='session'>An open TLS session</argument>
<argument name='hostname'>The hostname of the server connected to. This may be left blank, in which case only the validity of the certificate will be checked. It is much more secure to specify a hostname, so that it can be confirmed that the host connected to is the expected one.</argument>
<summary>Verify a server's certificate</summary>
<prose>Verify that the server connected to has a valid server certificate. This should be called before sending or receiving confidential data on the connection.</prose>
<related><functionref>acceptCertificate</functionref></related>"
public Void verifyCertificate(TLSsession session, String hostname="") {
  try {
    do_verifycert(getVM(),session.sess,hostname);
  } catch(InternalError(code)) {
    case code of {
      1 -> throw(VerifyFailed("Unexpected verification error"));
      | 2 -> throw(VerifyFailed("Signature was invalid"));
      | 3 -> throw(VerifyFailed("Certificate was revoked"));
      | 4 -> throw(VerifyFailed("Certificate signer untrusted"));
      | 5 -> throw(VerifyFailed("Hostname mismatch"));
      | 6 -> throw(VerifyFailed("Unexpected hostname verification error"));
    }
  }
}

"<argument name='tls'>A TLS session</argument>
<argument name='toput'>The unencrypted data to send</argument>
<summary>Send encrypted data</summary>
<prose>Send encrypted data. This function is called by <code>Net::send</code> as necessary.</prose>"
public Void putTLS(TLSsession tls, String toput) {
  put_TLS(tls.sess,toput);
}

"<argument name='tls'>A TLS session</argument>
<argument name='toput'>The unencrypted data to send (as a <dataref>Binary::Binary</dataref> set of bytes)</argument>
<summary>Send encrypted binary data</summary>
<prose>Send encrypted binary data. This function is called by <code>Net::sendBytes</code> as necessary.</prose>"
public Void putTLSBytes(TLSsession tls, Binary toput) {
  put_TLSbin(tls.sess,blockData(toput),blockSize(toput));
}

"<argument name='tls'>A TLS session</argument>
<argument name='toput'>The unencrypted byte to send</argument>
<summary>Send a single encrypted byte</summary>
<prose>Send a single encrypted byte. This function is called by <code>Net::sendByte</code> as necessary.</prose>"
public Void putTLSByte(TLSsession tls, Int toput) {
  put_TLSbyte(tls.sess,toput);
}

"<argument name='tls'>A TLS session</argument>
<argument name='iiscompensation'>Whether to compensate for bugs in the HTTPS handling in Microsoft IIS servers (optional, defaults to false)</argument>
<summary>Receive encrypted data</summary>
<prose>Receive encrypted data. This function is called by <code>Net::recv</code> as necessary.</prose>"
public String getTLS(TLSsession tls, Bool iiscompensation=false, Int maxlen=-1, Int timeout=0, Bool(Int) checktimeout) {
  output = "";
  if (maxlen==-1) {
    do {
      if (timeout==0 || checktimeout(timeout)) {
	temp = doTLSrecv(tls,iiscompensation,4096);
	output += temp;
      } else {
	if (output == "") {
	  throw(TLSTimeout);
	}
	temp = "";
      }
    } while (temp != "");
  } else {
    if (timeout==0 || checktimeout(timeout)) {
      output += doTLSrecv(tls,iiscompensation,4096);
    } else {
      throw(TLSTimeout);
    }
  }
  return output;
}

String doTLSrecv(TLSsession tls, Bool iiscompensation, Int maxlen) {
  vm = getVM();

  if (!iiscompensation) {
    temp = get_TLS(tls.sess,vm,maxlen);
  } else {
    try {
      temp = get_TLS(tls.sess,vm,maxlen);
    } catch(InternalError(code)) {
      if (code == -9) {
	temp = "";
      } else {
	throw(TLSFailed(code)); // re-throw, it's not the expected error
      }
    }
  }
  return temp;
}

"<argument name='tls'>A TLS session</argument>
<argument name='iiscompensation'>Whether to compensate for bugs in the HTTPS handling in Microsoft IIS servers (optional, defaults to false)</argument>
<summary>Receive encrypted binary data</summary>
<prose>Receive encrypted binary data. This function is called by <code>Net::recvBytes</code> as necessary.</prose>"
public Binary getTLSBytes(TLSsession tls, Bool iiscompensation=false) {
  vm = getVM();
  maxlen = 0;
  if (!iiscompensation) {
    ptr = get_TLSBytes(tls.sess,vm,maxlen);
  } else {
    try {
      ptr = get_TLSBytes(tls.sess,vm,maxlen);
    } catch(InternalError(code)) {
      if (code == -9) {
	return createBlock(0);
      } else {
	throw(TLSFailed(code)); // re-throw, it's not the expected error
      }
    }
  }
  return createInitialisedBlock(ptr,maxlen);
}

"<argument name='tls'>A TLS session</argument>
<argument name='iiscompensation'>Whether to compensate for bugs in the HTTPS handling in Microsoft IIS servers (optional, defaults to false)</argument>
<summary>Receive encrypted byte</summary>
<prose>Receive encrypted byte. This function is called by <code>Net::recvByte</code> as necessary. This function may return -1 if IIS bug compensation is on and the connection has been closed (if called via <code>recvByte</code> this will lead to a <exceptref>Net::NothingToReceive</exceptref> exception.</prose>"
public Int getTLSByte(TLSsession tls, Bool iiscompensation=false) {
  vm = getVM();
  if (!iiscompensation) {
    byte = get_TLSByte(tls.sess,vm);
  } else {
    try {
      byte = get_TLSByte(tls.sess,vm);
    } catch(InternalError(code)) {
      if (code == -9) {
	return -1;
      } else {
	throw(TLSFailed(code)); // re-throw, it's not the expected error
      }
    }
  }
  return byte;
}

"<argument name='tls'>A TLS session</argument>
<summary>Close TLS connection</summary>
<prose>Close a TLS connection. This function is called by <code>Net::closeConnection</code> as necessary.</prose>"
public Void closeTLS(TLSsession tls) {
  close_TLS(tls.sess,tls.cred);
}