|
1 # |
|
2 # This patch modifies the MIT implementation of krb5_fcc_initialize() so |
|
3 # it doesn't call unlink() on an existing ccache file. This modification |
|
4 # was done a long time ago in Solaris to workaround a race condition |
|
5 # brought on by the interaction between Solaris pam_krb5 and MIT's |
|
6 # implementation of krb5_fcc_initialize(). Given there are better ways of |
|
7 # fixing the race condition we will not give this patch to MIT however a |
|
8 # proper race condition fix would take prohibitively long to implement |
|
9 # hence this patch. When pam_krb5 is modified to better deal with the |
|
10 # ccache file and RFE 22229031 regarding ktkt_warnd is implemented then |
|
11 # this patch can be removed. |
|
12 # Patch source: in-house |
|
13 # |
|
14 |
|
15 diff -Naru old/src/lib/krb5/ccache/cc_file.c new/src/lib/krb5/ccache/cc_file.c |
|
16 --- old/src/lib/krb5/ccache/cc_file.c 2015-05-08 16:27:02.000000000 -0700 |
|
17 +++ new/src/lib/krb5/ccache/cc_file.c 2015-11-16 15:54:02.138183303 -0800 |
|
18 @@ -64,6 +64,10 @@ |
|
19 #include "k5-int.h" |
|
20 #include "cc-int.h" |
|
21 |
|
22 +/* Solaris Kerberos */ |
|
23 +#include <syslog.h> |
|
24 +#include <ctype.h> |
|
25 + |
|
26 #include <stdio.h> |
|
27 #include <errno.h> |
|
28 |
|
29 @@ -71,6 +75,11 @@ |
|
30 #include <unistd.h> |
|
31 #endif |
|
32 |
|
33 +/* Solaris Kerberos */ |
|
34 +/* How long to block if flock fails with EAGAIN */ |
|
35 +#define LOCK_RETRIES 100 |
|
36 +#define WAIT_LENGTH 20 /* in milliseconds */ |
|
37 + |
|
38 extern const krb5_cc_ops krb5_cc_file_ops; |
|
39 |
|
40 krb5_error_code krb5_change_cache(void); |
|
41 @@ -85,6 +94,7 @@ |
|
42 #define FCC_OPEN_AND_ERASE 1 |
|
43 #define FCC_OPEN_RDWR 2 |
|
44 #define FCC_OPEN_RDONLY 3 |
|
45 +#define FCC_OPEN_AND_ERASE_NOUNLINK 255 /* Solaris Kerberos */ |
|
46 |
|
47 #define FCC_TAG_DELTATIME 1 |
|
48 |
|
49 @@ -524,6 +534,130 @@ |
|
50 ((SIZE) < BUFSIZE ? (abort(),0) : setbuf(FILE, BUF)) |
|
51 #endif |
|
52 |
|
53 +/* Solaris Kerberos */ |
|
54 +static krb5_error_code |
|
55 +krb5_fcc_open_nounlink(char *filename, int open_flag, int *ret_fd, int *new) |
|
56 +{ |
|
57 + struct stat lres; |
|
58 + struct stat fres; |
|
59 + int error; |
|
60 + uid_t uid, euid; |
|
61 + int fd; |
|
62 + int newfile = 0; |
|
63 + |
|
64 + *ret_fd = -1; |
|
65 + /* |
|
66 + * Solaris Kerberos |
|
67 + * If we are opening in NOUNLINK mode, we have to check that the |
|
68 + * existing file, if any, is not a symlink. If it is, we try to |
|
69 + * delete and re-create it. |
|
70 + */ |
|
71 + error = lstat(filename, &lres); |
|
72 + if (error == -1 && errno != ENOENT) { |
|
73 + syslog(LOG_ERR, "lstat failed for %s [%m]", filename); |
|
74 + return (-1); |
|
75 + } |
|
76 + |
|
77 + if (error == 0 && !S_ISREG(lres.st_mode)) { |
|
78 + syslog(LOG_WARNING, "%s is not a plain file!", filename); |
|
79 + syslog(LOG_WARNING, "trying to unlink %s", filename); |
|
80 + if (unlink(filename) != 0) { |
|
81 + syslog(LOG_ERR, "could not unlink %s [%m]", filename); |
|
82 + return (-1); |
|
83 + } |
|
84 + } |
|
85 + |
|
86 + fd = THREEPARAMOPEN(filename, open_flag | O_NONBLOCK | O_NOFOLLOW, 0600); |
|
87 + if (fd == -1) { |
|
88 + if (errno == ENOENT) { |
|
89 + fd = THREEPARAMOPEN(filename, open_flag | O_EXCL | O_CREAT, |
|
90 + 0600); |
|
91 + if (fd != -1) { |
|
92 + newfile = 1; |
|
93 + } else { |
|
94 + /* If the file got created after the open we must retry */ |
|
95 + if (errno == EEXIST) |
|
96 + return (0); |
|
97 + } |
|
98 + } else if (errno == EACCES) { |
|
99 + /* |
|
100 + * We failed since the file existed with wrong permissions. |
|
101 + * Let's try to unlink it and if that succeeds retry. |
|
102 + */ |
|
103 + syslog(LOG_WARNING, "Insufficient permissions on %s", filename); |
|
104 + syslog(LOG_WARNING, "trying to unlink %s", filename); |
|
105 + if (unlink(filename) != 0) { |
|
106 + syslog(LOG_ERR, "could not unlink %s [%m]", filename); |
|
107 + return (-1); |
|
108 + } |
|
109 + return (0); |
|
110 + } |
|
111 + } |
|
112 + /* If we still don't have a valid fd, we stop trying */ |
|
113 + if (fd == -1) |
|
114 + return (-1); |
|
115 + |
|
116 + /* |
|
117 + * Solaris Kerberos |
|
118 + * If the file was not created now with a O_CREAT | O_EXCL open, |
|
119 + * we have opened an existing file. We should check if the file |
|
120 + * owner is us, if not, unlink and retry. If unlink fails we log |
|
121 + * the error and return. |
|
122 + */ |
|
123 + if (!newfile) { |
|
124 + if (fstat(fd, &fres) == -1) { |
|
125 + syslog(LOG_ERR, "lstat failed for %s [%m]", filename); |
|
126 + close(fd); |
|
127 + return (-1); |
|
128 + } |
|
129 + /* Check if this is the same file we lstat'd earlier */ |
|
130 + if (lres.st_dev != fres.st_dev || lres.st_ino != fres.st_ino) { |
|
131 + syslog(LOG_ERR, "%s changed between stat and open!", filename); |
|
132 + close(fd); |
|
133 + return (-1); |
|
134 + } |
|
135 + |
|
136 + /* |
|
137 + * Solaris Kerberos |
|
138 + * Check if the cc filename uid matches owner of file. |
|
139 + * Expects cc file to be in the form of /tmp/krb5cc_<uid>, |
|
140 + * else skip this check. |
|
141 + */ |
|
142 + if (strncmp(filename, "/tmp/krb5cc_", strlen("/tmp/krb5cc_")) == 0) { |
|
143 + uid_t fname_uid; |
|
144 + char *uidstr = strchr(filename, '_'); |
|
145 + char *s = NULL; |
|
146 + |
|
147 + /* make sure we have some non-null char after '_' */ |
|
148 + if (!*++uidstr) |
|
149 + goto out; |
|
150 + |
|
151 + /* make sure the uid part is all digits */ |
|
152 + for (s = uidstr; *s; s++) |
|
153 + if (!isdigit(*s)) |
|
154 + goto out; |
|
155 + |
|
156 + fname_uid = (uid_t) atoi(uidstr); |
|
157 + if (fname_uid != fres.st_uid) { |
|
158 + close(fd); |
|
159 + syslog(LOG_WARNING, "%s owned by %d instead of %d", |
|
160 + filename, fres.st_uid, fname_uid); |
|
161 + syslog(LOG_WARNING, "trying to unlink %s", filename); |
|
162 + if (unlink(filename) != 0) { |
|
163 + syslog(LOG_ERR, "could not unlink %s [%m]", filename); |
|
164 + return (-1); |
|
165 + } |
|
166 + return (0); |
|
167 + } |
|
168 + } |
|
169 + } |
|
170 + |
|
171 +out: |
|
172 + *new = newfile; |
|
173 + *ret_fd = fd; |
|
174 + return (0); |
|
175 +} |
|
176 + |
|
177 /* Open and lock the cache file. If mode is FCC_OPEN_AND_ERASE, initialize it |
|
178 * with a header. Call with the mutex locked. */ |
|
179 static krb5_error_code |
|
180 @@ -538,6 +672,10 @@ |
|
181 int f, open_flag, lock_flag, cnt; |
|
182 char buf[1024]; |
|
183 |
|
184 + /* Solaris Kerberos */ |
|
185 + int retries = 0; |
|
186 + int newfile = 0; |
|
187 + |
|
188 k5_cc_mutex_assert_locked(context, &data->lock); |
|
189 invalidate_cache(data); |
|
190 |
|
191 @@ -549,6 +687,10 @@ |
|
192 } |
|
193 |
|
194 switch (mode) { |
|
195 + /* Solaris Kerberos */ |
|
196 + case FCC_OPEN_AND_ERASE_NOUNLINK: |
|
197 + open_flag = O_RDWR; |
|
198 + break; |
|
199 case FCC_OPEN_AND_ERASE: |
|
200 unlink(data->filename); |
|
201 open_flag = O_CREAT | O_EXCL | O_TRUNC | O_RDWR; |
|
202 @@ -562,7 +704,21 @@ |
|
203 break; |
|
204 } |
|
205 |
|
206 +fcc_retry: |
|
207 + /* |
|
208 + * Solaris Kerberos |
|
209 + * If we are opening in NOUNLINK mode, check whether we are opening a |
|
210 + * symlink or a file owned by some other user and take preventive action. |
|
211 + */ |
|
212 + newfile = 0; |
|
213 + if (mode == FCC_OPEN_AND_ERASE_NOUNLINK) { |
|
214 + ret = krb5_fcc_open_nounlink(data->filename, open_flag, |
|
215 + &f, &newfile); |
|
216 + if (ret == 0 && f == -1) |
|
217 + goto fcc_retry; |
|
218 + } else { |
|
219 f = THREEPARAMOPEN(data->filename, open_flag | O_BINARY, 0600); |
|
220 + } |
|
221 if (f == NO_FILE) { |
|
222 if (errno == ENOENT) { |
|
223 ret = KRB5_FCC_NOFILE; |
|
224 @@ -584,10 +740,26 @@ |
|
225 ret = krb5_lock_file(context, f, lock_flag); |
|
226 if (ret) { |
|
227 (void)close(f); |
|
228 + if (ret == EAGAIN && retries++ < LOCK_RETRIES) { |
|
229 + /* Solaris Kerberos wait some time before retrying */ |
|
230 + if (poll(NULL, 0, WAIT_LENGTH) == 0) |
|
231 + goto fcc_retry; |
|
232 + } |
|
233 + syslog(LOG_ERR, "Failed to lock %s [%m]", data->filename); |
|
234 return ret; |
|
235 } |
|
236 |
|
237 - if (mode == FCC_OPEN_AND_ERASE) { |
|
238 + if (mode == FCC_OPEN_AND_ERASE || mode == FCC_OPEN_AND_ERASE_NOUNLINK) { |
|
239 + /* |
|
240 + * Solaris Kerberos |
|
241 + * If this file was not created, we have to flush existing data. |
|
242 + * This will happen only if we are doing an ERASE_NOUNLINK open. |
|
243 + */ |
|
244 + if (newfile == 0 && (ftruncate(f, 0) == -1)) { |
|
245 + syslog(LOG_ERR, "ftruncate failed for %s [%m]", data->filename); |
|
246 + close(f); |
|
247 + return (interpret_errno(context, errno)); |
|
248 + } |
|
249 /* write the version number */ |
|
250 store_16_be(context->fcc_default_format, fcc_fvno); |
|
251 data->version = context->fcc_default_format; |
|
252 @@ -755,14 +927,16 @@ |
|
253 |
|
254 k5_cc_mutex_lock(context, &data->lock); |
|
255 |
|
256 - MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE); |
|
257 + MAYBE_OPEN(context, id, FCC_OPEN_AND_ERASE_NOUNLINK); |
|
258 |
|
259 +#if 0 |
|
260 #if defined(HAVE_FCHMOD) || defined(HAVE_CHMOD) |
|
261 #ifdef HAVE_FCHMOD |
|
262 st = fchmod(data->fd, S_IRUSR | S_IWUSR); |
|
263 #else |
|
264 st = chmod(data->filename, S_IRUSR | S_IWUSR); |
|
265 #endif |
|
266 +#endif |
|
267 if (st == -1) { |
|
268 ret = interpret_errno(context, errno); |
|
269 MAYBE_CLOSE(context, id, ret); |