| 1 |
;;; Copyright (C) 2001, 2003 Eric Marsden |
|---|
| 2 |
;;; Copyright (C) 2005 David Lichteblau |
|---|
| 3 |
;;; "the conditions and ENSURE-SSL-FUNCALL are by Jochen Schmidt." |
|---|
| 4 |
;;; |
|---|
| 5 |
;;; See LICENSE for details. |
|---|
| 6 |
|
|---|
| 7 |
(eval-when (:compile-toplevel) |
|---|
| 8 |
(declaim |
|---|
| 9 |
(optimize (speed 3) (space 1) (safety 1) (debug 0) (compilation-speed 0)))) |
|---|
| 10 |
|
|---|
| 11 |
(in-package :cl+ssl) |
|---|
| 12 |
|
|---|
| 13 |
;;; Global state |
|---|
| 14 |
;;; |
|---|
| 15 |
(defvar *ssl-global-context* nil) |
|---|
| 16 |
(defvar *ssl-global-method* nil) |
|---|
| 17 |
(defvar *bio-lisp-method* nil) |
|---|
| 18 |
|
|---|
| 19 |
(defparameter *blockp* t) |
|---|
| 20 |
(defparameter *partial-read-p* nil) |
|---|
| 21 |
|
|---|
| 22 |
(defun ssl-initialized-p () |
|---|
| 23 |
(and *ssl-global-context* *ssl-global-method*)) |
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
;;; Constants |
|---|
| 27 |
;;; |
|---|
| 28 |
(defconstant +random-entropy+ 256) |
|---|
| 29 |
|
|---|
| 30 |
(defconstant +ssl-filetype-pem+ 1) |
|---|
| 31 |
(defconstant +ssl-filetype-asn1+ 2) |
|---|
| 32 |
(defconstant +ssl-filetype-default+ 3) |
|---|
| 33 |
|
|---|
| 34 |
(defconstant +SSL_CTRL_SET_SESS_CACHE_MODE+ 44) |
|---|
| 35 |
(defconstant +SSL_CTRL_MODE+ 33) |
|---|
| 36 |
|
|---|
| 37 |
(defconstant +SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER+ 2) |
|---|
| 38 |
|
|---|
| 39 |
;;; Misc |
|---|
| 40 |
;;; |
|---|
| 41 |
(defmacro while (cond &body body) |
|---|
| 42 |
`(do () ((not ,cond)) ,@body)) |
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
;;; Function definitions |
|---|
| 46 |
;;; |
|---|
| 47 |
(declaim (inline ssl-write ssl-read ssl-connect ssl-accept)) |
|---|
| 48 |
|
|---|
| 49 |
(cffi:defctype ssl-method :pointer) |
|---|
| 50 |
(cffi:defctype ssl-ctx :pointer) |
|---|
| 51 |
(cffi:defctype ssl-pointer :pointer) |
|---|
| 52 |
|
|---|
| 53 |
(cffi:defcfun ("SSL_get_version" ssl-get-version) |
|---|
| 54 |
:string |
|---|
| 55 |
(ssl ssl-pointer)) |
|---|
| 56 |
(cffi:defcfun ("SSL_load_error_strings" ssl-load-error-strings) |
|---|
| 57 |
:void) |
|---|
| 58 |
(cffi:defcfun ("SSL_library_init" ssl-library-init) |
|---|
| 59 |
:int) |
|---|
| 60 |
(cffi:defcfun ("SSLv2_client_method" ssl-v2-client-method) |
|---|
| 61 |
ssl-method) |
|---|
| 62 |
(cffi:defcfun ("SSLv23_client_method" ssl-v23-client-method) |
|---|
| 63 |
ssl-method) |
|---|
| 64 |
(cffi:defcfun ("SSLv23_server_method" ssl-v23-server-method) |
|---|
| 65 |
ssl-method) |
|---|
| 66 |
(cffi:defcfun ("SSLv23_method" ssl-v23-method) |
|---|
| 67 |
ssl-method) |
|---|
| 68 |
(cffi:defcfun ("SSLv3_client_method" ssl-v3-client-method) |
|---|
| 69 |
ssl-method) |
|---|
| 70 |
(cffi:defcfun ("SSLv3_server_method" ssl-v3-server-method) |
|---|
| 71 |
ssl-method) |
|---|
| 72 |
(cffi:defcfun ("SSLv3_method" ssl-v3-method) |
|---|
| 73 |
ssl-method) |
|---|
| 74 |
(cffi:defcfun ("TLSv1_client_method" ssl-TLSv1-client-method) |
|---|
| 75 |
ssl-method) |
|---|
| 76 |
(cffi:defcfun ("TLSv1_server_method" ssl-TLSv1-server-method) |
|---|
| 77 |
ssl-method) |
|---|
| 78 |
(cffi:defcfun ("TLSv1_method" ssl-TLSv1-method) |
|---|
| 79 |
ssl-method) |
|---|
| 80 |
|
|---|
| 81 |
(cffi:defcfun ("SSL_CTX_new" ssl-ctx-new) |
|---|
| 82 |
ssl-ctx |
|---|
| 83 |
(method ssl-method)) |
|---|
| 84 |
(cffi:defcfun ("SSL_new" ssl-new) |
|---|
| 85 |
ssl-pointer |
|---|
| 86 |
(ctx ssl-ctx)) |
|---|
| 87 |
(cffi:defcfun ("SSL_get_fd" ssl-get-fd) |
|---|
| 88 |
:int |
|---|
| 89 |
(ssl ssl-pointer)) |
|---|
| 90 |
(cffi:defcfun ("SSL_set_fd" ssl-set-fd) |
|---|
| 91 |
:int |
|---|
| 92 |
(ssl ssl-pointer) |
|---|
| 93 |
(fd :int)) |
|---|
| 94 |
(cffi:defcfun ("SSL_set_bio" ssl-set-bio) |
|---|
| 95 |
:void |
|---|
| 96 |
(ssl ssl-pointer) |
|---|
| 97 |
(rbio :pointer) |
|---|
| 98 |
(wbio :pointer)) |
|---|
| 99 |
(cffi:defcfun ("SSL_get_error" ssl-get-error) |
|---|
| 100 |
:int |
|---|
| 101 |
(ssl ssl-pointer) |
|---|
| 102 |
(ret :int)) |
|---|
| 103 |
(cffi:defcfun ("SSL_set_connect_state" ssl-set-connect-state) |
|---|
| 104 |
:void |
|---|
| 105 |
(ssl ssl-pointer)) |
|---|
| 106 |
(cffi:defcfun ("SSL_set_accept_state" ssl-set-accept-state) |
|---|
| 107 |
:void |
|---|
| 108 |
(ssl ssl-pointer)) |
|---|
| 109 |
(cffi:defcfun ("SSL_connect" ssl-connect) |
|---|
| 110 |
:int |
|---|
| 111 |
(ssl ssl-pointer)) |
|---|
| 112 |
(cffi:defcfun ("SSL_accept" ssl-accept) |
|---|
| 113 |
:int |
|---|
| 114 |
(ssl ssl-pointer)) |
|---|
| 115 |
(cffi:defcfun ("SSL_write" ssl-write) |
|---|
| 116 |
:int |
|---|
| 117 |
(ssl ssl-pointer) |
|---|
| 118 |
(buf :pointer) |
|---|
| 119 |
(num :int)) |
|---|
| 120 |
(cffi:defcfun ("SSL_read" ssl-read) |
|---|
| 121 |
:int |
|---|
| 122 |
(ssl ssl-pointer) |
|---|
| 123 |
(buf :pointer) |
|---|
| 124 |
(num :int)) |
|---|
| 125 |
(cffi:defcfun ("SSL_shutdown" ssh-shutdown) |
|---|
| 126 |
:void |
|---|
| 127 |
(ssl ssl-pointer)) |
|---|
| 128 |
(cffi:defcfun ("SSL_free" ssl-free) |
|---|
| 129 |
:void |
|---|
| 130 |
(ssl ssl-pointer)) |
|---|
| 131 |
(cffi:defcfun ("SSL_CTX_free" ssl-ctx-free) |
|---|
| 132 |
:void |
|---|
| 133 |
(ctx ssl-ctx)) |
|---|
| 134 |
(cffi:defcfun ("RAND_seed" rand-seed) |
|---|
| 135 |
:void |
|---|
| 136 |
(buf :pointer) |
|---|
| 137 |
(num :int)) |
|---|
| 138 |
(cffi:defcfun ("BIO_ctrl" bio-set-fd) |
|---|
| 139 |
:long |
|---|
| 140 |
(bio :pointer) |
|---|
| 141 |
(cmd :int) |
|---|
| 142 |
(larg :long) |
|---|
| 143 |
(parg :pointer)) |
|---|
| 144 |
(cffi:defcfun ("BIO_new_socket" bio-new-socket) |
|---|
| 145 |
:pointer |
|---|
| 146 |
(fd :int) |
|---|
| 147 |
(close-flag :int)) |
|---|
| 148 |
(cffi:defcfun ("BIO_new" bio-new) |
|---|
| 149 |
:pointer |
|---|
| 150 |
(method :pointer)) |
|---|
| 151 |
|
|---|
| 152 |
(cffi:defcfun ("ERR_get_error" err-get-error) |
|---|
| 153 |
:unsigned-long) |
|---|
| 154 |
(cffi:defcfun ("ERR_error_string" err-error-string) |
|---|
| 155 |
:string |
|---|
| 156 |
(e :unsigned-long) |
|---|
| 157 |
(buf :pointer)) |
|---|
| 158 |
|
|---|
| 159 |
(cffi:defcfun ("SSL_set_cipher_list" ssl-set-cipher-list) |
|---|
| 160 |
:int |
|---|
| 161 |
(ssl ssl-pointer) |
|---|
| 162 |
(str :string)) |
|---|
| 163 |
(cffi:defcfun ("SSL_use_RSAPrivateKey_file" ssl-use-rsa-privatekey-file) |
|---|
| 164 |
:int |
|---|
| 165 |
(ssl ssl-pointer) |
|---|
| 166 |
(str :string) |
|---|
| 167 |
;; either +ssl-filetype-pem+ or +ssl-filetype-asn1+ |
|---|
| 168 |
(type :int)) |
|---|
| 169 |
(cffi:defcfun |
|---|
| 170 |
("SSL_CTX_use_RSAPrivateKey_file" ssl-ctx-use-rsa-privatekey-file) |
|---|
| 171 |
:int |
|---|
| 172 |
(ctx ssl-ctx) |
|---|
| 173 |
(type :int)) |
|---|
| 174 |
(cffi:defcfun ("SSL_use_certificate_file" ssl-use-certificate-file) |
|---|
| 175 |
:int |
|---|
| 176 |
(ssl ssl-pointer) |
|---|
| 177 |
(str :string) |
|---|
| 178 |
(type :int)) |
|---|
| 179 |
(cffi:defcfun ("SSL_CTX_load_verify_locations" ssl-ctx-load-verify-locations) |
|---|
| 180 |
:int |
|---|
| 181 |
(ctx ssl-ctx) |
|---|
| 182 |
(CAfile :string) |
|---|
| 183 |
(CApath :string)) |
|---|
| 184 |
(cffi:defcfun ("SSL_CTX_set_client_CA_list" ssl-ctx-set-client-ca-list) |
|---|
| 185 |
:void |
|---|
| 186 |
(ctx ssl-ctx) |
|---|
| 187 |
(list ssl-pointer)) |
|---|
| 188 |
(cffi:defcfun ("SSL_load_client_CA_file" ssl-load-client-ca-file) |
|---|
| 189 |
ssl-pointer |
|---|
| 190 |
(file :string)) |
|---|
| 191 |
|
|---|
| 192 |
(cffi:defcfun ("SSL_CTX_ctrl" ssl-ctx-ctrl) |
|---|
| 193 |
:long |
|---|
| 194 |
(ctx ssl-ctx) |
|---|
| 195 |
(cmd :int) |
|---|
| 196 |
(larg :long) |
|---|
| 197 |
(parg :long)) |
|---|
| 198 |
|
|---|
| 199 |
|
|---|
| 200 |
;;; Funcall wrapper |
|---|
| 201 |
;;; |
|---|
| 202 |
(defvar *socket*) |
|---|
| 203 |
|
|---|
| 204 |
(declaim (inline ensure-ssl-funcall)) |
|---|
| 205 |
(defun ensure-ssl-funcall (stream handle func &rest args) |
|---|
| 206 |
(loop |
|---|
| 207 |
(let ((nbytes |
|---|
| 208 |
(let ((*socket* stream)) ;for Lisp-BIO callbacks |
|---|
| 209 |
(apply func args)))) |
|---|
| 210 |
(when (plusp nbytes) |
|---|
| 211 |
(return nbytes)) |
|---|
| 212 |
(let ((error (ssl-get-error handle nbytes))) |
|---|
| 213 |
(case error |
|---|
| 214 |
(#.+ssl-error-want-read+ |
|---|
| 215 |
(input-wait stream |
|---|
| 216 |
(ssl-get-fd handle) |
|---|
| 217 |
(ssl-stream-deadline stream))) |
|---|
| 218 |
(#.+ssl-error-want-write+ |
|---|
| 219 |
(output-wait stream |
|---|
| 220 |
(ssl-get-fd handle) |
|---|
| 221 |
(ssl-stream-deadline stream))) |
|---|
| 222 |
(t |
|---|
| 223 |
(ssl-signal-error handle func error nbytes))))))) |
|---|
| 224 |
|
|---|
| 225 |
|
|---|
| 226 |
;;; Waiting for output to be possible |
|---|
| 227 |
|
|---|
| 228 |
#+clozure-common-lisp |
|---|
| 229 |
(defun milliseconds-until-deadline (deadline stream) |
|---|
| 230 |
(let* ((now (get-internal-real-time))) |
|---|
| 231 |
(if (> now deadline) |
|---|
| 232 |
(error 'ccl::communication-deadline-expired :stream stream) |
|---|
| 233 |
(values |
|---|
| 234 |
(round (- deadline now) (/ internal-time-units-per-second 1000)))))) |
|---|
| 235 |
|
|---|
| 236 |
#+clozure-common-lisp |
|---|
| 237 |
(defun output-wait (stream fd deadline) |
|---|
| 238 |
(unless deadline |
|---|
| 239 |
(setf deadline (stream-deadline (ssl-stream-socket stream)))) |
|---|
| 240 |
(let* ((timeout |
|---|
| 241 |
(if deadline |
|---|
| 242 |
(milliseconds-until-deadline deadline stream) |
|---|
| 243 |
nil))) |
|---|
| 244 |
(multiple-value-bind (win timedout error) |
|---|
| 245 |
(ccl::process-output-wait fd timeout) |
|---|
| 246 |
(unless win |
|---|
| 247 |
(if timedout |
|---|
| 248 |
(error 'ccl::communication-deadline-expired :stream stream) |
|---|
| 249 |
(ccl::stream-io-error stream (- error) "write")))))) |
|---|
| 250 |
|
|---|
| 251 |
#+sbcl |
|---|
| 252 |
(defun output-wait (stream fd deadline) |
|---|
| 253 |
(declare (ignore stream)) |
|---|
| 254 |
(let ((timeout |
|---|
| 255 |
;; *deadline* is handled by wait-until-fd-usable automatically, |
|---|
| 256 |
;; but we need to turn a user-specified deadline into a timeout |
|---|
| 257 |
(when deadline |
|---|
| 258 |
(/ (- deadline (get-internal-real-time)) |
|---|
| 259 |
internal-time-units-per-second)))) |
|---|
| 260 |
(sb-sys:wait-until-fd-usable fd :output timeout))) |
|---|
| 261 |
|
|---|
| 262 |
#-(or clozure-common-lisp sbcl) |
|---|
| 263 |
(defun output-wait (stream fd deadline) |
|---|
| 264 |
(declare (ignore stream fd deadline)) |
|---|
| 265 |
;; This situation means that the lisp set our fd to non-blocking mode, |
|---|
| 266 |
;; and streams.lisp didn't know how to undo that. |
|---|
| 267 |
(warn "non-blocking stream encountered unexpectedly")) |
|---|
| 268 |
|
|---|
| 269 |
|
|---|
| 270 |
;;; Waiting for input to be possible |
|---|
| 271 |
|
|---|
| 272 |
#+clozure-common-lisp |
|---|
| 273 |
(defun input-wait (stream fd deadline) |
|---|
| 274 |
(unless deadline |
|---|
| 275 |
(setf deadline (stream-deadline (ssl-stream-socket stream)))) |
|---|
| 276 |
(let* ((timeout |
|---|
| 277 |
(if deadline |
|---|
| 278 |
(milliseconds-until-deadline deadline stream) |
|---|
| 279 |
nil))) |
|---|
| 280 |
(multiple-value-bind (win timedout error) |
|---|
| 281 |
(ccl::process-input-wait fd timeout) |
|---|
| 282 |
(unless win |
|---|
| 283 |
(if timedout |
|---|
| 284 |
(error 'ccl::communication-deadline-expired :stream stream) |
|---|
| 285 |
(ccl::stream-io-error stream (- error) "read")))))) |
|---|
| 286 |
|
|---|
| 287 |
#+sbcl |
|---|
| 288 |
(defun input-wait (stream fd deadline) |
|---|
| 289 |
(declare (ignore stream)) |
|---|
| 290 |
(let ((timeout |
|---|
| 291 |
;; *deadline* is handled by wait-until-fd-usable automatically, |
|---|
| 292 |
;; but we need to turn a user-specified deadline into a timeout |
|---|
| 293 |
(when deadline |
|---|
| 294 |
(/ (- deadline (get-internal-real-time)) |
|---|
| 295 |
internal-time-units-per-second)))) |
|---|
| 296 |
(sb-sys:wait-until-fd-usable fd :input timeout))) |
|---|
| 297 |
|
|---|
| 298 |
#-(or clozure-common-lisp sbcl) |
|---|
| 299 |
(defun input-wait (stream fd deadline) |
|---|
| 300 |
(declare (ignore stream fd deadline)) |
|---|
| 301 |
;; This situation means that the lisp set our fd to non-blocking mode, |
|---|
| 302 |
;; and streams.lisp didn't know how to undo that. |
|---|
| 303 |
(warn "non-blocking stream encountered unexpectedly")) |
|---|
| 304 |
|
|---|
| 305 |
|
|---|
| 306 |
;;; Initialization |
|---|
| 307 |
;;; |
|---|
| 308 |
(defun init-prng () |
|---|
| 309 |
;; this initialization of random entropy is not necessary on |
|---|
| 310 |
;; Linux, since the OpenSSL library automatically reads from |
|---|
| 311 |
;; /dev/urandom if it exists. On Solaris it is necessary. |
|---|
| 312 |
(let ((buf (cffi-sys::make-shareable-byte-vector +random-entropy+))) |
|---|
| 313 |
(dotimes (i +random-entropy+) |
|---|
| 314 |
(setf (elt buf i) (random 256))) |
|---|
| 315 |
(cffi-sys::with-pointer-to-vector-data (ptr buf) |
|---|
| 316 |
(rand-seed ptr +random-entropy+)))) |
|---|
| 317 |
|
|---|
| 318 |
(defun ssl-ctx-set-session-cache-mode (ctx mode) |
|---|
| 319 |
(ssl-ctx-ctrl ctx +SSL_CTRL_SET_SESS_CACHE_MODE+ mode 0)) |
|---|
| 320 |
|
|---|
| 321 |
(defun initialize (&optional (method 'ssl-v23-method)) |
|---|
| 322 |
(setf *bio-lisp-method* (make-bio-lisp-method)) |
|---|
| 323 |
(ssl-load-error-strings) |
|---|
| 324 |
(ssl-library-init) |
|---|
| 325 |
(init-prng) |
|---|
| 326 |
(setf *ssl-global-method* (funcall method)) |
|---|
| 327 |
(setf *ssl-global-context* (ssl-ctx-new *ssl-global-method*)) |
|---|
| 328 |
(ssl-ctx-set-session-cache-mode *ssl-global-context* 3)) |
|---|
| 329 |
|
|---|
| 330 |
(defun ensure-initialized (&optional (method 'ssl-v23-method)) |
|---|
| 331 |
(unless (ssl-initialized-p) |
|---|
| 332 |
(initialize method)) |
|---|
| 333 |
(unless *bio-lisp-method* |
|---|
| 334 |
(setf *bio-lisp-method* (make-bio-lisp-method)))) |
|---|
| 335 |
|
|---|
| 336 |
(defun reload () |
|---|
| 337 |
(cffi:load-foreign-library 'libssl) |
|---|
| 338 |
(cffi:load-foreign-library 'libeay32) |
|---|
| 339 |
(setf *ssl-global-context* nil) |
|---|
| 340 |
(setf *ssl-global-method* nil)) |
|---|