xref: /aosp_15_r20/external/boringssl/src/crypto/bio/file.c (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1 /* Copyright (C) 1995-1998 Eric Young ([email protected])
2  * All rights reserved.
3  *
4  * This package is an SSL implementation written
5  * by Eric Young ([email protected]).
6  * The implementation was written so as to conform with Netscapes SSL.
7  *
8  * This library is free for commercial and non-commercial use as long as
9  * the following conditions are aheared to.  The following conditions
10  * apply to all code found in this distribution, be it the RC4, RSA,
11  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
12  * included with this distribution is covered by the same copyright terms
13  * except that the holder is Tim Hudson ([email protected]).
14  *
15  * Copyright remains Eric Young's, and as such any Copyright notices in
16  * the code are not to be removed.
17  * If this package is used in a product, Eric Young should be given attribution
18  * as the author of the parts of the library used.
19  * This can be in the form of a textual message at program startup or
20  * in documentation (online or textual) provided with the package.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  * 1. Redistributions of source code must retain the copyright
26  *    notice, this list of conditions and the following disclaimer.
27  * 2. Redistributions in binary form must reproduce the above copyright
28  *    notice, this list of conditions and the following disclaimer in the
29  *    documentation and/or other materials provided with the distribution.
30  * 3. All advertising materials mentioning features or use of this software
31  *    must display the following acknowledgement:
32  *    "This product includes cryptographic software written by
33  *     Eric Young ([email protected])"
34  *    The word 'cryptographic' can be left out if the rouines from the library
35  *    being used are not cryptographic related :-).
36  * 4. If you include any Windows specific code (or a derivative thereof) from
37  *    the apps directory (application code) you must include an acknowledgement:
38  *    "This product includes software written by Tim Hudson ([email protected])"
39  *
40  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
41  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
44  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  *
52  * The licence and distribution terms for any publically available version or
53  * derivative of this code cannot be changed.  i.e. this code cannot simply be
54  * copied and put under another distribution licence
55  * [including the GNU Public Licence.] */
56 
57 #if defined(__linux) || defined(__sun) || defined(__hpux)
58 // Following definition aliases fopen to fopen64 on above mentioned
59 // platforms. This makes it possible to open and sequentially access
60 // files larger than 2GB from 32-bit application. It does not allow to
61 // traverse them beyond 2GB with fseek/ftell, but on the other hand *no*
62 // 32-bit platform permits that, not with fseek/ftell. Not to mention
63 // that breaking 2GB limit for seeking would require surgery to *our*
64 // API. But sequential access suffices for practical cases when you
65 // can run into large files, such as fingerprinting, so we can let API
66 // alone. For reference, the list of 32-bit platforms which allow for
67 // sequential access of large files without extra "magic" comprise *BSD,
68 // Darwin, IRIX...
69 #ifndef _FILE_OFFSET_BITS
70 #define _FILE_OFFSET_BITS 64
71 #endif
72 #endif
73 
74 #include <openssl/bio.h>
75 
76 #include <assert.h>
77 #include <errno.h>
78 #include <stdio.h>
79 #include <string.h>
80 
81 #include <openssl/err.h>
82 #include <openssl/mem.h>
83 
84 #include "../internal.h"
85 
86 #if defined(OPENSSL_WINDOWS)
87 #include <fcntl.h>
88 #include <io.h>
89 #endif
90 
91 #define BIO_FP_READ 0x02
92 #define BIO_FP_WRITE 0x04
93 #define BIO_FP_APPEND 0x08
94 
95 #if !defined(OPENSSL_NO_FILESYSTEM)
96 #define fopen_if_available fopen
97 #else
fopen_if_available(const char * path,const char * mode)98 static FILE *fopen_if_available(const char *path, const char *mode) {
99   errno = ENOENT;
100   return NULL;
101 }
102 #endif
103 
BIO_new_file(const char * filename,const char * mode)104 BIO *BIO_new_file(const char *filename, const char *mode) {
105   BIO *ret;
106   FILE *file;
107 
108   file = fopen_if_available(filename, mode);
109   if (file == NULL) {
110     OPENSSL_PUT_SYSTEM_ERROR();
111 
112     ERR_add_error_data(5, "fopen('", filename, "','", mode, "')");
113     if (errno == ENOENT) {
114       OPENSSL_PUT_ERROR(BIO, BIO_R_NO_SUCH_FILE);
115     } else {
116       OPENSSL_PUT_ERROR(BIO, BIO_R_SYS_LIB);
117     }
118     return NULL;
119   }
120 
121   ret = BIO_new_fp(file, BIO_CLOSE);
122   if (ret == NULL) {
123     fclose(file);
124     return NULL;
125   }
126 
127   return ret;
128 }
129 
BIO_new_fp(FILE * stream,int flags)130 BIO *BIO_new_fp(FILE *stream, int flags) {
131   BIO *ret = BIO_new(BIO_s_file());
132   if (ret == NULL) {
133     return NULL;
134   }
135 
136   BIO_set_fp(ret, stream, flags);
137   return ret;
138 }
139 
file_free(BIO * bio)140 static int file_free(BIO *bio) {
141   if (!bio->shutdown) {
142     return 1;
143   }
144 
145   if (bio->init && bio->ptr != NULL) {
146     fclose(bio->ptr);
147     bio->ptr = NULL;
148   }
149   bio->init = 0;
150 
151   return 1;
152 }
153 
file_read(BIO * b,char * out,int outl)154 static int file_read(BIO *b, char *out, int outl) {
155   if (!b->init) {
156     return 0;
157   }
158 
159   size_t ret = fread(out, 1, outl, (FILE *)b->ptr);
160   if (ret == 0 && ferror((FILE *)b->ptr)) {
161     OPENSSL_PUT_SYSTEM_ERROR();
162     OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
163     return -1;
164   }
165 
166   // fread reads at most |outl| bytes, so |ret| fits in an int.
167   return (int)ret;
168 }
169 
file_write(BIO * b,const char * in,int inl)170 static int file_write(BIO *b, const char *in, int inl) {
171   if (!b->init) {
172     return 0;
173   }
174 
175   int ret = (int)fwrite(in, inl, 1, (FILE *)b->ptr);
176   if (ret > 0) {
177     ret = inl;
178   }
179   return ret;
180 }
181 
file_ctrl(BIO * b,int cmd,long num,void * ptr)182 static long file_ctrl(BIO *b, int cmd, long num, void *ptr) {
183   long ret = 1;
184   FILE *fp = (FILE *)b->ptr;
185   FILE **fpp;
186 
187   switch (cmd) {
188     case BIO_CTRL_RESET:
189       num = 0;
190       OPENSSL_FALLTHROUGH;
191     case BIO_C_FILE_SEEK:
192       ret = (long)fseek(fp, num, 0);
193       break;
194     case BIO_CTRL_EOF:
195       ret = (long)feof(fp);
196       break;
197     case BIO_C_FILE_TELL:
198     case BIO_CTRL_INFO:
199       ret = ftell(fp);
200       break;
201     case BIO_C_SET_FILE_PTR:
202       file_free(b);
203       static_assert((BIO_CLOSE & BIO_FP_TEXT) == 0,
204                     "BIO_CLOSE and BIO_FP_TEXT must not collide");
205 #if defined(OPENSSL_WINDOWS)
206       // If |BIO_FP_TEXT| is not set, OpenSSL will switch the file to binary
207       // mode. BoringSSL intentionally diverges here because it means code
208       // tested under POSIX will inadvertently change the state of |FILE|
209       // objects when wrapping them in a |BIO|.
210       if (num & BIO_FP_TEXT) {
211         _setmode(_fileno(ptr), _O_TEXT);
212       }
213 #endif
214       b->shutdown = (int)num & BIO_CLOSE;
215       b->ptr = ptr;
216       b->init = 1;
217       break;
218     case BIO_C_SET_FILENAME:
219       file_free(b);
220       b->shutdown = (int)num & BIO_CLOSE;
221       const char *mode;
222       if (num & BIO_FP_APPEND) {
223         if (num & BIO_FP_READ) {
224           mode = "ab+";
225         } else {
226           mode = "ab";
227         }
228       } else if ((num & BIO_FP_READ) && (num & BIO_FP_WRITE)) {
229         mode = "rb+";
230       } else if (num & BIO_FP_WRITE) {
231         mode = "wb";
232       } else if (num & BIO_FP_READ) {
233         mode = "rb";
234       } else {
235         OPENSSL_PUT_ERROR(BIO, BIO_R_BAD_FOPEN_MODE);
236         ret = 0;
237         break;
238       }
239       fp = fopen_if_available(ptr, mode);
240       if (fp == NULL) {
241         OPENSSL_PUT_SYSTEM_ERROR();
242         ERR_add_error_data(5, "fopen('", ptr, "','", mode, "')");
243         OPENSSL_PUT_ERROR(BIO, ERR_R_SYS_LIB);
244         ret = 0;
245         break;
246       }
247       b->ptr = fp;
248       b->init = 1;
249       break;
250     case BIO_C_GET_FILE_PTR:
251       // the ptr parameter is actually a FILE ** in this case.
252       if (ptr != NULL) {
253         fpp = (FILE **)ptr;
254         *fpp = (FILE *)b->ptr;
255       }
256       break;
257     case BIO_CTRL_GET_CLOSE:
258       ret = (long)b->shutdown;
259       break;
260     case BIO_CTRL_SET_CLOSE:
261       b->shutdown = (int)num;
262       break;
263     case BIO_CTRL_FLUSH:
264       ret = 0 == fflush((FILE *)b->ptr);
265       break;
266     case BIO_CTRL_WPENDING:
267     case BIO_CTRL_PENDING:
268     default:
269       ret = 0;
270       break;
271   }
272   return ret;
273 }
274 
file_gets(BIO * bp,char * buf,int size)275 static int file_gets(BIO *bp, char *buf, int size) {
276   if (size == 0) {
277     return 0;
278   }
279 
280   if (!fgets(buf, size, (FILE *)bp->ptr)) {
281     buf[0] = 0;
282     // TODO(davidben): This doesn't distinguish error and EOF. This should check
283     // |ferror| as in |file_read|.
284     return 0;
285   }
286 
287   return (int)strlen(buf);
288 }
289 
290 static const BIO_METHOD methods_filep = {
291     BIO_TYPE_FILE,   "FILE pointer",
292     file_write,      file_read,
293     NULL /* puts */, file_gets,
294     file_ctrl,       NULL /* create */,
295     file_free,       NULL /* callback_ctrl */,
296 };
297 
BIO_s_file(void)298 const BIO_METHOD *BIO_s_file(void) { return &methods_filep; }
299 
300 
BIO_get_fp(BIO * bio,FILE ** out_file)301 int BIO_get_fp(BIO *bio, FILE **out_file) {
302   return (int)BIO_ctrl(bio, BIO_C_GET_FILE_PTR, 0, (char *)out_file);
303 }
304 
BIO_set_fp(BIO * bio,FILE * file,int flags)305 int BIO_set_fp(BIO *bio, FILE *file, int flags) {
306   return (int)BIO_ctrl(bio, BIO_C_SET_FILE_PTR, flags, (char *)file);
307 }
308 
BIO_read_filename(BIO * bio,const char * filename)309 int BIO_read_filename(BIO *bio, const char *filename) {
310   return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_READ,
311                        (char *)filename);
312 }
313 
BIO_write_filename(BIO * bio,const char * filename)314 int BIO_write_filename(BIO *bio, const char *filename) {
315   return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_WRITE,
316                        (char *)filename);
317 }
318 
BIO_append_filename(BIO * bio,const char * filename)319 int BIO_append_filename(BIO *bio, const char *filename) {
320   return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME, BIO_CLOSE | BIO_FP_APPEND,
321                        (char *)filename);
322 }
323 
BIO_rw_filename(BIO * bio,const char * filename)324 int BIO_rw_filename(BIO *bio, const char *filename) {
325   return (int)BIO_ctrl(bio, BIO_C_SET_FILENAME,
326                        BIO_CLOSE | BIO_FP_READ | BIO_FP_WRITE,
327                        (char *)filename);
328 }
329 
BIO_tell(BIO * bio)330 long BIO_tell(BIO *bio) { return BIO_ctrl(bio, BIO_C_FILE_TELL, 0, NULL); }
331 
BIO_seek(BIO * bio,long offset)332 long BIO_seek(BIO *bio, long offset) {
333   return BIO_ctrl(bio, BIO_C_FILE_SEEK, offset, NULL);
334 }
335