Add support for GPG signature enforcement on booted
[petitboot] / lib / file / file.c
1 /*
2  *  Copyright (C) 2013 Jeremy Kerr <jk@ozlabs.org>
3  *  Copyright (C) 2016 Raptor Engineering, LLC
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; version 2 of the License.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25
26 #include <talloc/talloc.h>
27 #include <log/log.h>
28
29 #include "file.h"
30
31 #define MAX_FILENAME_SIZE       8192
32 #define FILE_XFER_BUFFER_SIZE   8192
33
34 static const int max_file_size = 1024 * 1024;
35
36 int copy_file_secure_dest(void *ctx,
37         const char *source_file, char **destination_file) {
38         int result = 0;
39         char template[] = "/tmp/petitbootXXXXXX";
40         char dest_filename[MAX_FILENAME_SIZE] = "";
41         FILE *source_handle = fopen(source_file, "r");
42         int destination_fd = mkstemp(template);
43         FILE *destination_handle = fdopen(destination_fd, "w");
44         if (!source_handle || !(destination_handle)) {
45                 // handle open error
46                 pb_log("%s: failed: unable to open source file '%s'\n",
47                         __func__, source_file);
48                 return -1;
49         }
50
51         size_t l1;
52         unsigned char *buffer;
53         buffer = talloc_array(ctx, unsigned char, FILE_XFER_BUFFER_SIZE);
54         if (!buffer) {
55                 pb_log("%s: failed: unable to allocate file transfer buffer\n",
56                         __func__);
57                 return -1;
58         }
59
60         /* Copy data */
61         while ((l1 = fread(buffer, 1, sizeof buffer, source_handle)) > 0) {
62                 size_t l2 = fwrite(buffer, 1, l1, destination_handle);
63                 if (l2 < l1) {
64                         if (ferror(destination_handle)) {
65                                 /* General error */
66                                 result = -1;
67                                 pb_log("%s: failed: unknown fault\n", __func__);
68                         }
69                         else {
70                                 /* No space on destination device */
71                                 result = -1;
72                                 pb_log("%s: failed: temporary storage full\n",
73                                         __func__);
74                         }
75                         break;
76                 }
77         }
78
79         talloc_free(buffer);
80
81         if (result) {
82                 dest_filename[0] = '\0';
83         }
84         else {
85                 ssize_t r;
86                 char readlink_buffer[MAX_FILENAME_SIZE];
87                 snprintf(readlink_buffer, MAX_FILENAME_SIZE, "/proc/self/fd/%d",
88                         destination_fd);
89                 r = readlink(readlink_buffer, dest_filename,
90                         MAX_FILENAME_SIZE);
91                 if (r < 0) {
92                         /* readlink failed */
93                         result = -1;
94                         pb_log("%s: failed: unable to obtain temporary filename"
95                                 "\n", __func__);
96                 }
97                 dest_filename[r] = '\0';
98         }
99
100         fclose(source_handle);
101         fclose(destination_handle);
102
103         *destination_file = talloc_strdup(ctx, dest_filename);
104
105         return result;
106 }
107
108 int read_file(void *ctx, const char *filename, char **bufp, int *lenp)
109 {
110         struct stat statbuf;
111         int rc, fd, i, len;
112         char *buf;
113
114         fd = open(filename, O_RDONLY);
115         if (fd < 0)
116                 return -1;
117
118         rc = fstat(fd, &statbuf);
119         if (rc < 0)
120                 goto err_close;
121
122         len = statbuf.st_size;
123         if (len > max_file_size)
124                 goto err_close;
125
126         buf = talloc_array(ctx, char, len + 1);
127         if (!buf)
128                 goto err_close;
129
130         for (i = 0; i < len; i += rc) {
131                 rc = read(fd, buf + i, len - i);
132
133                 /* unexpected EOF: trim and return */
134                 if (rc == 0) {
135                         len = i;
136                         break;
137                 }
138
139                 if (rc < 0)
140                         goto err_free;
141
142         }
143
144         buf[len] = '\0';
145
146         close(fd);
147         *bufp = buf;
148         *lenp = len;
149         return 0;
150
151 err_free:
152         talloc_free(buf);
153 err_close:
154         close(fd);
155         return -1;
156 }
157
158 static int write_fd(int fd, char *buf, int len)
159 {
160         int i, rc;
161
162         for (i = 0; i < len; i += rc) {
163                 rc = write(fd, buf + i, len - i);
164                 if (rc < 0 && errno != -EINTR)
165                         return rc;
166         }
167
168         return 0;
169 }
170
171 int replace_file(const char *filename, char *buf, int len)
172 {
173         char *tempfile;
174         mode_t oldmask;
175         int rc, fd;
176
177         tempfile = talloc_asprintf(NULL, "%s.XXXXXX", filename);
178
179         oldmask = umask(0644);
180         fd = mkstemp(tempfile);
181         umask(oldmask);
182         if (fd < 0) {
183                 talloc_free(tempfile);
184                 return fd;
185         }
186
187         rc = write_fd(fd, buf, len);
188         if (rc) {
189                 unlink(tempfile);
190         } else {
191                 rc = rename(tempfile, filename);
192         }
193
194         talloc_free(tempfile);
195
196         fchmod(fd, 0644);
197
198         close(fd);
199         return rc;
200 }