Coverage Report

Created: 2025-06-11 06:41

/src/boringssl/crypto/bio/file.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#if defined(__linux) || defined(__sun) || defined(__hpux)
16
// Following definition aliases fopen to fopen64 on above mentioned
17
// platforms. This makes it possible to open and sequentially access
18
// files larger than 2GB from 32-bit application. It does not allow to
19
// traverse them beyond 2GB with fseek/ftell, but on the other hand *no*
20
// 32-bit platform permits that, not with fseek/ftell. Not to mention
21
// that breaking 2GB limit for seeking would require surgery to *our*
22
// API. But sequential access suffices for practical cases when you
23
// can run into large files, such as fingerprinting, so we can let API
24
// alone. For reference, the list of 32-bit platforms which allow for
25
// sequential access of large files without extra "magic" comprise *BSD,
26
// Darwin, IRIX...
27
#ifndef _FILE_OFFSET_BITS
28
#define _FILE_OFFSET_BITS 64
29
#endif
30
#endif
31
32
#include <openssl/bio.h>
33
34
#include <assert.h>
35
#include <errno.h>
36
#include <stdio.h>
37
#include <string.h>
38
39
#include <openssl/err.h>
40
#include <openssl/mem.h>
41
42
#include "../internal.h"
43
#include "internal.h"
44
45
#if defined(OPENSSL_WINDOWS)
46
#include <fcntl.h>
47
#include <io.h>
48
#endif
49
50
0
#define BIO_FP_READ 0x02
51
0
#define BIO_FP_WRITE 0x04
52
0
#define BIO_FP_APPEND 0x08
53
54
#if !defined(OPENSSL_NO_FILESYSTEM)
55
0
#define fopen_if_available fopen
56
#else
57
static FILE *fopen_if_available(const char *path, const char *mode) {
58
  errno = ENOENT;
59
  return NULL;
60
}
61
#endif
62
63
0
BIO *BIO_new_file(const char *filename, const char *mode) {
64
0
  BIO *ret;
65
0
  FILE *file;
66
67
0
  file = fopen_if_available(filename, mode);
68
0
  if (file == NULL) {
69
0
    OPENSSL_PUT_SYSTEM_ERROR();
70
71
0
    ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
72
0
    if (errno == ENOENT) {
73
0
      OPENSSL_PUT_ERROR(BIO, BIO_R_NO_SUCH_FILE);
74
0
    } else {
75
0
      OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
76
0
    }
77
0
    return NULL;
78
0
  }
79
80
0
  ret = BIO_new_fp(file, BIO_CLOSE);
81
0
  if (ret == NULL) {
82
0
    fclose(file);
83
0
    return NULL;
84
0
  }
85
86
0
  return ret;
87
0
}
88
89
0
BIO *BIO_new_fp(FILE *stream, int flags) {
90
0
  BIO *ret = BIO_new(BIO_s_file());
91
0
  if (ret == NULL) {
92
0
    return NULL;
93
0
  }
94
95
0
  BIO_set_fp(ret, stream, flags);
96
0
  return ret;
97
0
}
98
99
0
static int file_free(BIO *bio) {
100
0
  if (!bio->shutdown) {
101
0
    return 1;
102
0
  }
103
104
0
  if (bio->init && bio->ptr != NULL) {
105
0
    fclose(reinterpret_cast<FILE *>(bio->ptr));
106
0
    bio->ptr = NULL;
107
0
  }
108
0
  bio->init = 0;
109
110
0
  return 1;
111
0
}
112
113
0
static int file_read(BIO *b, char *out, int outl) {
114
0
  if (!b->init) {
115
0
    return 0;
116
0
  }
117
118
0
  size_t ret = fread(out, 1, outl, (FILE *)b->ptr);
119
0
  if (ret == 0 && ferror((FILE *)b->ptr)) {
120
0
    OPENSSL_PUT_SYSTEM_ERROR();
121
0
    OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
122
0
    return -1;
123
0
  }
124
125
  // fread reads at most |outl| bytes, so |ret| fits in an int.
126
0
  return (int)ret;
127
0
}
128
129
0
static int file_write(BIO *b, const char *in, int inl) {
130
0
  if (!b->init) {
131
0
    return 0;
132
0
  }
133
134
0
  int ret = (int)fwrite(in, inl, 1, (FILE *)b->ptr);
135
0
  if (ret > 0) {
136
0
    ret = inl;
137
0
  }
138
0
  return ret;
139
0
}
140
141
0
static long file_ctrl(BIO *b, int cmd, long num, void *ptr) {
142
0
  FILE *fp = static_cast<FILE *>(b->ptr);
143
0
  switch (cmd) {
144
0
    case BIO_CTRL_RESET:
145
0
      num = 0;
146
0
      [[fallthrough]];
147
0
    case BIO_C_FILE_SEEK:
148
0
      return fseek(fp, num, 0);
149
0
    case BIO_CTRL_EOF:
150
0
      return feof(fp);
151
0
    case BIO_C_FILE_TELL:
152
0
    case BIO_CTRL_INFO:
153
0
      return ftell(fp);
154
0
    case BIO_C_SET_FILE_PTR:
155
0
      file_free(b);
156
0
      static_assert((BIO_CLOSE & BIO_FP_TEXT) == 0,
157
0
                    "BIO_CLOSE and BIO_FP_TEXT must not collide");
158
#if defined(OPENSSL_WINDOWS)
159
      // If |BIO_FP_TEXT| is not set, OpenSSL will switch the file to binary
160
      // mode. BoringSSL intentionally diverges here because it means code
161
      // tested under POSIX will inadvertently change the state of |FILE|
162
      // objects when wrapping them in a |BIO|.
163
      if (num & BIO_FP_TEXT) {
164
        _setmode(_fileno(reinterpret_cast<FILE *>(ptr)), _O_TEXT);
165
      }
166
#endif
167
0
      b->shutdown = static_cast<int>(num) & BIO_CLOSE;
168
0
      b->ptr = ptr;
169
0
      b->init = 1;
170
0
      return 1;
171
0
    case BIO_C_SET_FILENAME:
172
0
      file_free(b);
173
0
      b->shutdown = static_cast<int>(num) & BIO_CLOSE;
174
0
      const char *mode;
175
0
      if (num & BIO_FP_APPEND) {
176
0
        if (num & BIO_FP_READ) {
177
0
          mode = "ab+";
178
0
        } else {
179
0
          mode = "ab";
180
0
        }
181
0
      } else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE)) {
182
0
        mode = "rb+";
183
0
      } else if (num & BIO_FP_WRITE) {
184
0
        mode = "wb";
185
0
      } else if (num & BIO_FP_READ) {
186
0
        mode = "rb";
187
0
      } else {
188
0
        OPENSSL_PUT_ERROR(BIO, BIO_R_BAD_FOPEN_MODE);
189
0
        return 0;
190
0
      }
191
0
      fp = fopen_if_available(reinterpret_cast<const char *>(ptr), mode);
192
0
      if (fp == nullptr) {
193
0
        OPENSSL_PUT_SYSTEM_ERROR();
194
0
        ERR_add_error_data(5, "fopen('", ptr, "','", mode, "')");
195
0
        OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
196
0
        return 0;
197
0
      }
198
0
      b->ptr = fp;
199
0
      b->init = 1;
200
0
      return 1;
201
0
    case BIO_C_GET_FILE_PTR:
202
      // the ptr parameter is actually a FILE ** in this case.
203
0
      if (ptr != nullptr) {
204
0
        FILE **out = static_cast<FILE **>(ptr);
205
0
        *out = fp;
206
0
      }
207
0
      return 1;
208
0
    case BIO_CTRL_GET_CLOSE:
209
0
      return b->shutdown;
210
0
    case BIO_CTRL_SET_CLOSE:
211
0
      b->shutdown = static_cast<int>(num);
212
0
      return 1;
213
0
    case BIO_CTRL_FLUSH:
214
0
      return fflush(fp) == 0;
215
0
    default:
216
0
      return 0;
217
0
  }
218
0
}
219
220
0
static int file_gets(BIO *bp, char *buf, int size) {
221
0
  if (size == 0) {
222
0
    return 0;
223
0
  }
224
225
0
  if (!fgets(buf, size, (FILE *)bp->ptr)) {
226
0
    buf[0] = 0;
227
    // TODO(davidben): This doesn't distinguish error and EOF. This should check
228
    // |ferror| as in |file_read|.
229
0
    return 0;
230
0
  }
231
232
0
  return (int)strlen(buf);
233
0
}
234
235
static const BIO_METHOD methods_filep = {
236
    BIO_TYPE_FILE,      "FILE pointer", file_write,
237
    file_read,          file_gets,      file_ctrl,
238
    /*create=*/nullptr, file_free,      /*callback_ctrl=*/nullptr,
239
};
240
241
0
const BIO_METHOD *BIO_s_file(void) { return &methods_filep; }
242
243
244
0
int BIO_get_fp(BIO *bio, FILE **out_file) {
245
0
  return (int)BIO_ctrl(bio, BIO_C_GET_FILE_PTR, 0, (char *)out_file);
246
0
}
247
248
0
int BIO_set_fp(BIO *bio, FILE *file, int flags) {
249
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILE_PTR, flags, (char *)file);
250
0
}
251
252
0
int BIO_read_filename(BIO *bio, const char *filename) {
253
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_READ,
254
0
                       (char *)filename);
255
0
}
256
257
0
int BIO_write_filename(BIO *bio, const char *filename) {
258
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_WRITE,
259
0
                       (char *)filename);
260
0
}
261
262
0
int BIO_append_filename(BIO *bio, const char *filename) {
263
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_APPEND,
264
0
                       (char *)filename);
265
0
}
266
267
0
int BIO_rw_filename(BIO *bio, const char *filename) {
268
0
  return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME,
269
0
                       BIO_CLOSE | BIO_FP_READ | BIO_FP_WRITE,
270
0
                       (char *)filename);
271
0
}
272
273
0
long BIO_tell(BIO *bio) { return BIO_ctrl(bio, BIO_C_FILE_TELL, 0, NULL); }
274
275
0
long BIO_seek(BIO *bio, long offset) {
276
0
  return BIO_ctrl(bio, BIO_C_FILE_SEEK, offset, NULL);
277
0
}