1 /*
2  *   stunnel       TLS offloading and load-balancing proxy
3  *   Copyright (C) 1998-2021 Michal Trojnara <Michal.Trojnara@stunnel.org>
4  *
5  *   This program is free software; you can redistribute it and/or modify it
6  *   under the terms of the GNU General Public License as published by the
7  *   Free Software Foundation; either version 2 of the License, or (at your
8  *   option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  *   See the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License along
16  *   with this program; if not, see <http://www.gnu.org/licenses>.
17  *
18  *   Linking stunnel statically or dynamically with other modules is making
19  *   a combined work based on stunnel. Thus, the terms and conditions of
20  *   the GNU General Public License cover the whole combination.
21  *
22  *   In addition, as a special exception, the copyright holder of stunnel
23  *   gives you permission to combine stunnel with free software programs or
24  *   libraries that are released under the GNU LGPL and with code included
25  *   in the standard release of OpenSSL under the OpenSSL License (or
26  *   modified versions of such code, with unchanged license). You may copy
27  *   and distribute such a system following the terms of the GNU GPL for
28  *   stunnel and the licenses of the other code concerned.
29  *
30  *   Note that people who make modified versions of stunnel are not obligated
31  *   to grant this special exception for their modified versions; it is their
32  *   choice whether to do so. The GNU General Public License gives permission
33  *   to release a modified version without this exception; this exception
34  *   also makes it possible to release a modified version which carries
35  *   forward this exception.
36  */
37 
38 #include "common.h"
39 #include "prototypes.h"
40 
41 #ifdef USE_WIN32
42 
file_open(char * name,FILE_MODE mode)43 DISK_FILE *file_open(char *name, FILE_MODE mode) {
44     DISK_FILE *df;
45     LPTSTR tname;
46     HANDLE fh;
47     DWORD desired_access, creation_disposition;
48 
49     /* open file */
50     switch(mode) {
51     case FILE_MODE_READ:
52         desired_access=GENERIC_READ;
53         creation_disposition=OPEN_EXISTING;
54         break;
55     case FILE_MODE_APPEND:
56             /* reportedly more compatible than FILE_APPEND_DATA */
57         desired_access=GENERIC_WRITE;
58         creation_disposition=OPEN_ALWAYS; /* keep the data */
59         break;
60     case FILE_MODE_OVERWRITE:
61         desired_access=GENERIC_WRITE;
62         creation_disposition=CREATE_ALWAYS; /* remove the data */
63         break;
64     default: /* invalid mode */
65         return NULL;
66     }
67     tname=str2tstr(name);
68     fh=CreateFile(tname, desired_access, FILE_SHARE_READ, NULL,
69         creation_disposition, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
70     str_free(tname); /* str_free() overwrites GetLastError() value */
71     if(fh==INVALID_HANDLE_VALUE)
72         return NULL;
73     if(mode==FILE_MODE_APPEND) /* workaround for FILE_APPEND_DATA */
74         SetFilePointer(fh, 0, NULL, FILE_END);
75 
76     /* setup df structure */
77     df=str_alloc(sizeof(DISK_FILE));
78     df->fh=fh;
79     return df;
80 }
81 
82 #else /* USE_WIN32 */
83 
file_fdopen(int fd)84 DISK_FILE *file_fdopen(int fd) {
85     DISK_FILE *df;
86 
87     df=str_alloc(sizeof(DISK_FILE));
88     df->fd=fd;
89     return df;
90 }
91 
file_open(char * name,FILE_MODE mode)92 DISK_FILE *file_open(char *name, FILE_MODE mode) {
93     DISK_FILE *df;
94     int fd, flags;
95 
96     /* open file */
97     switch(mode) {
98     case FILE_MODE_READ:
99         flags=O_RDONLY;
100         break;
101     case FILE_MODE_APPEND:
102         flags=O_CREAT|O_WRONLY|O_APPEND;
103         break;
104     case FILE_MODE_OVERWRITE:
105         flags=O_CREAT|O_WRONLY|O_TRUNC;
106         break;
107     default: /* invalid mode */
108         return NULL;
109     }
110 #ifdef O_NONBLOCK
111     flags|=O_NONBLOCK;
112 #elif defined O_NDELAY
113     flags|=O_NDELAY;
114 #endif
115 #ifdef O_CLOEXEC
116     flags|=O_CLOEXEC;
117 #endif /* O_CLOEXEC */
118     fd=open(name, flags, 0640);
119     if(fd==INVALID_SOCKET)
120         return NULL;
121 
122     /* setup df structure */
123     df=str_alloc(sizeof(DISK_FILE));
124     df->fd=fd;
125     return df;
126 }
127 
128 #endif /* USE_WIN32 */
129 
file_close(DISK_FILE * df)130 void file_close(DISK_FILE *df) {
131     if(!df) /* nothing to do */
132         return;
133 #ifdef USE_WIN32
134     CloseHandle(df->fh);
135 #else /* USE_WIN32 */
136     if(df->fd>2) /* never close stdin/stdout/stder */
137         close(df->fd);
138 #endif /* USE_WIN32 */
139     str_free(df);
140 }
141 
file_getline(DISK_FILE * df,char * line,int len)142 ssize_t file_getline(DISK_FILE *df, char *line, int len) {
143     /* this version is really slow, but performance is not important here */
144     /* (no buffering is implemented) */
145     ssize_t i;
146 #ifdef USE_WIN32
147     DWORD num;
148 #else /* USE_WIN32 */
149     ssize_t num;
150 #endif /* USE_WIN32 */
151 
152     if(!df) /* not opened */
153         return -1;
154 
155     for(i=0; i<len-1; i++) {
156 #ifdef USE_WIN32
157         ReadFile(df->fh, line+i, 1, &num, NULL);
158 #else /* USE_WIN32 */
159         num=read(df->fd, line+i, 1);
160 #endif /* USE_WIN32 */
161         if(num!=1) { /* EOF */
162             if(i) /* any previously retrieved data */
163                 break;
164             else
165                 return -1;
166         }
167         if(line[i]=='\n') /* LF */
168             break;
169         if(line[i]=='\r') /* CR */
170             --i; /* ignore - it must be the last check */
171     }
172     line[i]='\0';
173     return i;
174 }
175 
file_putline(DISK_FILE * df,char * line)176 ssize_t file_putline(DISK_FILE *df, char *line) {
177     char *buff;
178     size_t len;
179 #ifdef USE_WIN32
180     DWORD num;
181 #else /* USE_WIN32 */
182     ssize_t num;
183 #endif /* USE_WIN32 */
184 
185     len=strlen(line);
186     buff=str_alloc(len+2); /* +2 for CR+LF */
187     strcpy(buff, line);
188 #ifdef USE_WIN32
189     buff[len++]='\r'; /* CR */
190 #endif /* USE_WIN32 */
191     buff[len++]='\n'; /* LF */
192 #ifdef USE_WIN32
193     WriteFile(df->fh, buff, (DWORD)len, &num, NULL);
194 #else /* USE_WIN32 */
195     /* no file -> write to stderr */
196     num=write(df ? df->fd : 2, buff, len);
197 #endif /* USE_WIN32 */
198     str_free(buff);
199     return (ssize_t)num;
200 }
201 
file_permissions(const char * file_name)202 int file_permissions(const char *file_name) {
203 #if !defined(USE_WIN32) && !defined(USE_OS2)
204     struct stat sb; /* buffer for stat */
205 
206     /* check permissions of the private key file */
207     if(stat(file_name, &sb)) {
208         ioerror(file_name);
209         return 1; /* FAILED */
210     }
211     if(sb.st_mode & 7)
212         s_log(LOG_WARNING,
213             "Insecure file permissions on %s", file_name);
214 #else
215     (void)file_name; /* squash the unused parameter warning */
216 #endif
217     return 0;
218 }
219 
220 #ifdef USE_WIN32
221 
str2tstr(LPCSTR in)222 LPTSTR str2tstr(LPCSTR in) {
223     LPTSTR out;
224 #ifdef UNICODE
225     int len;
226 
227     len=MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
228     if(!len)
229         return str_tprintf(TEXT("MultiByteToWideChar() failed"));
230     out=str_alloc(((size_t)len+1)*sizeof(WCHAR));
231     len=MultiByteToWideChar(CP_UTF8, 0, in, -1, out, len);
232     if(!len) {
233         str_free(out);
234         return str_tprintf(TEXT("MultiByteToWideChar() failed"));
235     }
236 #else
237     /* FIXME: convert UTF-8 to native codepage */
238     out=str_dup(in);
239 #endif
240     return out;
241 }
242 
tstr2str(LPCTSTR in)243 LPSTR tstr2str(LPCTSTR in) {
244     LPSTR out;
245 #ifdef UNICODE
246     int len;
247 
248     len=WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
249     if(!len)
250         return str_printf("WideCharToMultiByte() failed");
251     out=str_alloc((size_t)len+1);
252     len=WideCharToMultiByte(CP_UTF8, 0, in, -1, out, len, NULL, NULL);
253     if(!len) {
254         str_free(out);
255         return str_printf("WideCharToMultiByte() failed");
256     }
257 #else
258     /* FIXME: convert native codepage to UTF-8 */
259     out=str_dup(in);
260 #endif
261     return out;
262 }
263 
264 #endif /* USE_WIN32 */
265 
266 /* end of file.c */
267