/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Copyright (c) 1999 Ng Pheng Siong. All rights reserved. * * Portions created by Open Source Applications Foundation (OSAF) are * Copyright (C) 2004-2005 OSAF. All Rights Reserved. * Author: Heikki Toivonen * * Copyright 2018 Daniel Wozniak. All Rights Reserved.*/ /* $Id$ */ %{ #include %} %apply Pointer NONNULL { BIO * }; %apply Pointer NONNULL { BIO_METHOD * }; %rename(bio_s_bio) BIO_s_bio; extern BIO_METHOD *BIO_s_bio(void); %rename(bio_s_mem) BIO_s_mem; extern BIO_METHOD *BIO_s_mem(void); %rename(bio_s_socket) BIO_s_socket; extern BIO_METHOD *BIO_s_socket(void); %rename(bio_f_ssl) BIO_f_ssl; extern BIO_METHOD *BIO_f_ssl(void); %rename(bio_f_buffer) BIO_f_buffer; extern BIO_METHOD *BIO_f_buffer(void); %rename(bio_f_cipher) BIO_f_cipher; extern BIO_METHOD *BIO_f_cipher(void); %rename(bio_new) BIO_new; extern BIO *BIO_new(BIO_METHOD *); %rename(bio_new_socket) BIO_new_socket; extern BIO *BIO_new_socket(int, int); %rename(bio_new_fd) BIO_new_pyfd; %rename(bio_new_pyfd) BIO_new_pyfd; %rename(bio_free_all) BIO_free_all; %threadallow BIO_free_all; extern void BIO_free_all(BIO *); %rename(bio_dup_chain) BIO_dup_chain; extern BIO *BIO_dup_chain(BIO *); %rename(bio_push) BIO_push; extern BIO *BIO_push(BIO *, BIO *); %rename(bio_pop) BIO_pop; extern BIO *BIO_pop(BIO *); %rename(bio_eof) BIO_eof; extern int BIO_eof(BIO *); %constant int bio_noclose = BIO_NOCLOSE; %constant int bio_close = BIO_CLOSE; %constant int BIO_FLAGS_READ = 0x01; %constant int BIO_FLAGS_WRITE = 0x02; %constant int BIO_FLAGS_IO_SPECIAL = 0x04; %constant int BIO_FLAGS_RWS = (BIO_FLAGS_READ|BIO_FLAGS_WRITE|BIO_FLAGS_IO_SPECIAL); %constant int BIO_FLAGS_SHOULD_RETRY = 0x08; %constant int BIO_FLAGS_MEM_RDONLY = 0x200; %warnfilter(454) _bio_err; %inline %{ static PyObject *_bio_err; void pyfd_init(void); void bio_init(PyObject *bio_err) { Py_INCREF(bio_err); _bio_err = bio_err; pyfd_init(); } int bio_free(BIO *bio) { int ret; Py_BEGIN_ALLOW_THREADS ret = BIO_free(bio); Py_END_ALLOW_THREADS if (ret == 0) { m2_PyErr_Msg(_bio_err); } return ret; } BIO * bio_new_file(const char *filename, const char *mode) { BIO *ret; Py_BEGIN_ALLOW_THREADS ret = BIO_new_file(filename, mode); Py_END_ALLOW_THREADS if (ret == NULL) { m2_PyErr_Msg(_bio_err); } return ret; } BIO *bio_new_pyfile(PyObject *pyfile, int bio_close) { FILE *fp = NULL; BIO *bio = NULL; fp = PyFile_AsFile(pyfile); bio = BIO_new_fp(fp, bio_close); /* returns NULL if error occurred */ if (bio == NULL) { /* Find out the name of the file so we can have good error * message. */ PyObject *pyname = m2_PyFile_Name(pyfile); char *name = PyBytes_AsString(pyname); if (name == NULL) { PyErr_Format(_bio_err, "Opening of the new BIO on file failed!"); } else { PyErr_Format(_bio_err, "Opening of the new BIO on file %s failed!", name); } Py_DECREF(pyname); } return bio; } PyObject *bio_read(BIO *bio, int num) { PyObject *blob; void *buf; int r; if (!(buf = PyMem_Malloc(num))) { PyErr_SetString(PyExc_MemoryError, "bio_read"); return NULL; } Py_BEGIN_ALLOW_THREADS r = BIO_read(bio, buf, num); Py_END_ALLOW_THREADS if (r < 0) { PyMem_Free(buf); if (ERR_peek_error()) { m2_PyErr_Msg(_bio_err); return NULL; } Py_RETURN_NONE; } blob = PyBytes_FromStringAndSize(buf, r); PyMem_Free(buf); return blob; } PyObject *bio_gets(BIO *bio, int num) { PyObject *blob; void *buf; int r; if (!(buf = PyMem_Malloc(num))) { PyErr_SetString(PyExc_MemoryError, "bio_gets"); return NULL; } Py_BEGIN_ALLOW_THREADS r = BIO_gets(bio, buf, num); Py_END_ALLOW_THREADS if (r < 1) { PyMem_Free(buf); if (ERR_peek_error()) { m2_PyErr_Msg(_bio_err); return NULL; } Py_RETURN_NONE; } blob = PyBytes_FromStringAndSize(buf, r); PyMem_Free(buf); return blob; } int bio_write(BIO *bio, PyObject *from) { const void *fbuf; int flen = 0, ret; if (m2_PyObject_AsReadBufferInt(from, &fbuf, &flen) == -1) return -1; Py_BEGIN_ALLOW_THREADS ret = BIO_write(bio, fbuf, flen); Py_END_ALLOW_THREADS if (ret < 0) { if (ERR_peek_error()) { m2_PyErr_Msg(_bio_err); return -1; } } return ret; } /* XXX Casting size_t to int. */ int bio_ctrl_pending(BIO *bio) { return (int)BIO_ctrl_pending(bio); } int bio_ctrl_wpending(BIO *bio) { return (int)BIO_ctrl_wpending(bio); } int bio_ctrl_get_write_guarantee(BIO *a) { return BIO_ctrl_get_write_guarantee(a); } int bio_reset(BIO *bio) { return (int)BIO_reset(bio); } %} %threadallow bio_flush; %inline %{ int bio_flush(BIO *bio) { return (int)BIO_flush(bio); } int bio_seek(BIO *bio, int offset) { return (int)BIO_seek(bio, offset); } int bio_tell(BIO* bio) { return BIO_tell(bio); } void bio_set_flags(BIO *bio, int flags) { BIO_set_flags(bio, flags); } int bio_get_flags(BIO *bio) { return BIO_get_flags(bio); } /* * sets the cipher of BIO @param b to c using key @param key and IV @iv. * @param enc should be set to 1 for encryption and zero to decryption. * */ PyObject *bio_set_cipher(BIO *b, EVP_CIPHER *c, PyObject *key, PyObject *iv, int op) { const void *kbuf, *ibuf; Py_ssize_t klen, ilen; if ((PyObject_AsReadBuffer(key, &kbuf, &klen) == -1) || (PyObject_AsReadBuffer(iv, &ibuf, &ilen) == -1)) return NULL; BIO_set_cipher(b, (const EVP_CIPHER *)c, (unsigned char *)kbuf, (unsigned char *)ibuf, op); Py_RETURN_NONE; } int bio_set_mem_eof_return(BIO *b, int v) { return (int)BIO_set_mem_eof_return(b, v); } int bio_get_fd(BIO *bio) { return BIO_get_fd(bio, NULL); } %} %warnfilter(454) methods_fdp; %threadallow bio_do_handshake; %inline %{ int bio_do_handshake(BIO *bio) { return BIO_do_handshake(bio); } /* macro */ int bio_make_bio_pair(BIO* b1, BIO* b2) { return BIO_make_bio_pair(b1, b2); } int bio_set_write_buf_size(BIO* b, size_t size) { return BIO_set_write_buf_size(b, size); } int bio_should_retry(BIO* a) { return BIO_should_retry(a); } int bio_should_read(BIO* a) { return BIO_should_read(a); } int bio_should_write(BIO* a) { return BIO_should_write(a); } /* Macros for things not defined before 1.1.0 */ #if OPENSSL_VERSION_NUMBER < 0x10100000L static BIO_METHOD * BIO_meth_new( int type, const char *name ) { BIO_METHOD *method = malloc( sizeof(BIO_METHOD) ); memset( method, 0, sizeof(BIO_METHOD) ); method->type = type; method->name = name; return method; } static void BIO_meth_free( BIO_METHOD *meth ) { if ( meth == NULL ) { return; } free(meth); } #define BIO_meth_set_write(m, f) (m)->bwrite = (f) #define BIO_meth_set_read(m, f) (m)->bread = (f) #define BIO_meth_set_puts(m, f) (m)->bputs = (f) #define BIO_meth_set_gets(m, f) (m)->bgets = (f) #define BIO_meth_set_ctrl(m, f) (m)->ctrl = (f) #define BIO_meth_set_create(m, f) (m)->create = (f) #define BIO_meth_set_destroy(m, f) (m)->destroy = (f) #define BIO_set_shutdown(b, x) (b)->shutdown = x #define BIO_get_shutdown(b) (b)->shutdown #define BIO_set_init(b, x) b->init = x #define BIO_get_init(b) (b)->init #define BIO_set_data(b, x) b->ptr = x #define BIO_clear_flags(b, x) b->flags &= ~(x) #define BIO_get_data(b) b->ptr #endif /* implment custom BIO_s_pyfd */ #ifdef _WIN32 # define clear_sys_error() SetLastError(0) /* Linux doesn't use underscored calls yet */ # define open(p, f, m) _open(p, f, m) # define read(f, b, n) _read(f, b, n) # define write(f, b, n) _write(f, b, n) # define close(f) _close(f) # define lseek(fd, o, w) _lseek(fd, o, w) #else # define clear_sys_error() errno=0 #endif typedef struct pyfd_struct { int fd; } BIO_PYFD_CTX; /* Setting up methods_fdp */ static BIO_METHOD *methods_fdp; static int pyfd_write(BIO *b, const char *in, int inl) { int ret, fd; if (BIO_get_fd(b, &fd) == -1) { PyErr_SetString(_bio_err, "BIO has not been initialized."); return -1; } clear_sys_error(); ret = write(fd, in, inl); BIO_clear_retry_flags(b); if (ret <= 0) { if (BIO_fd_should_retry(ret)) BIO_set_retry_write(b); } return ret; } static int pyfd_read(BIO *b, char *out, int outl) { int ret = 0, fd; if (BIO_get_fd(b, &fd) == -1) { PyErr_SetString(_bio_err, "BIO has not been initialized."); return -1; } if (out != NULL) { clear_sys_error(); ret = read(fd, out, outl); BIO_clear_retry_flags(b); if (ret <= 0) { if (BIO_fd_should_retry(ret)) BIO_set_retry_read(b); } } return ret; } static int pyfd_puts(BIO *bp, const char *str) { int n, ret; n = strlen(str); ret = pyfd_write(bp, str, n); return ret; } static int pyfd_gets(BIO *bp, char *buf, int size) { int ret = 0; char *ptr = buf; char *end = buf + size - 1; /* See https://github.com/openssl/openssl/pull/3442 We were here just repeating a bug from OpenSSL */ while (ptr < end && pyfd_read(bp, ptr, 1) > 0) { if (*ptr++ == '\n') break; } ptr[0] = '\0'; if (buf[0] != '\0') ret = strlen(buf); return ret; } static int pyfd_new(BIO* b) { BIO_PYFD_CTX* ctx; ctx = OPENSSL_zalloc(sizeof(*ctx)); if (ctx == NULL) return 0; ctx->fd = -1; BIO_set_data(b, ctx); BIO_set_shutdown(b, 0); BIO_set_init(b, 1); return 1; } static int pyfd_free(BIO* b) { BIO_PYFD_CTX* ctx; if (b == 0) return 0; ctx = BIO_get_data(b); if (ctx == NULL) return 0; if (BIO_get_shutdown(b) && BIO_get_init(b)) close(ctx->fd); BIO_set_data(b, NULL); BIO_set_shutdown(b, 0); BIO_set_init(b, 0); OPENSSL_free(ctx); return 1; } static long pyfd_ctrl(BIO *b, int cmd, long num, void *ptr) { BIO_PYFD_CTX* ctx; int *ip; long ret = 1; ctx = BIO_get_data(b); if (ctx == NULL) return 0; switch (cmd) { case BIO_CTRL_RESET: num = 0; case BIO_C_FILE_SEEK: ret = (long)lseek(ctx->fd, num, 0); break; case BIO_C_FILE_TELL: case BIO_CTRL_INFO: ret = (long)lseek(ctx->fd, 0, 1); break; case BIO_C_SET_FD: pyfd_free(b); if (*((int *)ptr) > -1) { if (!pyfd_new(b) || !(ctx = BIO_get_data(b))) return 0; ctx->fd = *((int *)ptr); BIO_set_shutdown(b, (int)num); BIO_set_init(b, 1); } break; case BIO_C_GET_FD: if (BIO_get_init(b)) { ip = (int *)ptr; if (ip != NULL) *ip = ctx->fd; ret = ctx->fd; } else ret = -1; break; case BIO_CTRL_GET_CLOSE: ret = BIO_get_shutdown(b); break; case BIO_CTRL_SET_CLOSE: BIO_set_shutdown(b, (int)num); break; case BIO_CTRL_PENDING: case BIO_CTRL_WPENDING: ret = 0; break; case BIO_CTRL_DUP: case BIO_CTRL_FLUSH: ret = 1; break; default: ret = 0; break; } return ret; } void pyfd_init(void) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L methods_fdp = BIO_meth_new( BIO_get_new_index()|BIO_TYPE_DESCRIPTOR|BIO_TYPE_SOURCE_SINK, "python file descriptor"); #else methods_fdp = BIO_meth_new( 100 |BIO_TYPE_DESCRIPTOR|BIO_TYPE_SOURCE_SINK, "python file descriptor"); #endif BIO_meth_set_write(methods_fdp, pyfd_write); BIO_meth_set_read(methods_fdp, pyfd_read); BIO_meth_set_puts(methods_fdp, pyfd_puts); BIO_meth_set_gets(methods_fdp, pyfd_gets); BIO_meth_set_ctrl(methods_fdp, pyfd_ctrl); BIO_meth_set_create(methods_fdp, pyfd_new); BIO_meth_set_destroy(methods_fdp, pyfd_free); } BIO* BIO_new_pyfd(int fd, int close_flag) { BIO *ret; ret = BIO_new(methods_fdp); BIO_set_fd(ret, fd, close_flag); return ret; } %}