1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
/* move.c
Move a file.
Copyright (C) 1991, 1992, 1993 Ian Lance Taylor
This file is part of the Taylor UUCP package.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
The author of the program may be contacted at ian@airs.com or
c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
*/
#include "uucp.h"
#include "uudefs.h"
#include "sysdep.h"
#include "system.h"
#include <errno.h>
#if HAVE_FCNTL_H
#include <fcntl.h>
#else
#if HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
#endif
/* Move (rename) a file from one name to another. This routine will
optionally create necessary directories, and fpublic indicates
whether the new directories should be publically accessible or not.
If fcheck is true, it will try to determine whether the named user
has write access to the new file. */
boolean
fsysdep_move_file (zorig, zto, fmkdirs, fpublic, fcheck, zuser)
const char *zorig;
const char *zto;
boolean fmkdirs;
boolean fpublic;
boolean fcheck;
const char *zuser;
{
struct stat s;
int o;
DEBUG_MESSAGE2 (DEBUG_SPOOLDIR,
"fsysdep_move_file: Moving %s to %s", zorig, zto);
/* Optionally make sure that zuser has write access on the
directory. */
if (fcheck)
{
char *zcopy;
char *zslash;
zcopy = zbufcpy (zto);
zslash = strrchr (zcopy, '/');
if (zslash == zcopy)
zslash[1] = '\0';
else
*zslash = '\0';
if (stat (zcopy, &s) != 0)
{
ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno));
ubuffree (zcopy);
return FALSE;
}
if (! fsuser_access (&s, W_OK, zuser))
{
ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES));
ubuffree (zcopy);
return FALSE;
}
ubuffree (zcopy);
/* A malicious user now has a few milliseconds to change a
symbolic link to a directory uucp has write permission on but
the user does not (the obvious choice being /usr/lib/uucp).
The only certain method I can come up with to close this race
is to fork an suid process which takes on the users identity
and does the actual copy. This is sufficiently high overhead
that I'm not going to do it. */
}
/* We try to use rename to move the file. */
if (rename (zorig, zto) == 0)
return TRUE;
if (fmkdirs && errno == ENOENT)
{
if (! fsysdep_make_dirs (zto, fpublic))
return FALSE;
if (rename (zorig, zto) == 0)
return TRUE;
}
#if HAVE_RENAME
/* On some systems the system call rename seems to fail for
arbitrary reasons. To get around this, we always try to copy the
file by hand if the rename failed. */
errno = EXDEV;
#endif
/* If we can't link across devices, we must copy the file by hand. */
if (errno != EXDEV)
{
ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto,
strerror (errno));
return FALSE;
}
/* Copy the file. */
if (stat ((char *) zorig, &s) < 0)
{
ulog (LOG_ERROR, "stat (%s): %s", zorig, strerror (errno));
return FALSE;
}
/* Make sure the file gets the right mode by creating it before we
call fcopy_file. */
(void) remove (zto);
o = creat ((char *) zto, s.st_mode);
if (o < 0)
{
if (fmkdirs && errno == ENOENT)
{
if (! fsysdep_make_dirs (zto, fpublic))
return FALSE;
o = creat ((char *) zto, s.st_mode);
}
if (o < 0)
{
ulog (LOG_ERROR, "creat (%s): %s", zto, strerror (errno));
return FALSE;
}
}
(void) close (o);
if (! fcopy_file (zorig, zto, fpublic, fmkdirs, FALSE))
return FALSE;
if (remove (zorig) != 0)
ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno));
return TRUE;
}
|