1 |
|
|
/* $OpenBSD: common.c,v 1.37 2015/12/22 08:48:39 mmcc Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 1983 Regents of the University of California. |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
16 |
|
|
* may be used to endorse or promote products derived from this software |
17 |
|
|
* without specific prior written permission. |
18 |
|
|
* |
19 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
20 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
21 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
23 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
25 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
26 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
27 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 |
|
|
* SUCH DAMAGE. |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
#include <sys/stat.h> |
33 |
|
|
#include <sys/time.h> |
34 |
|
|
#include <sys/wait.h> |
35 |
|
|
|
36 |
|
|
#include <errno.h> |
37 |
|
|
#include <fcntl.h> |
38 |
|
|
#include <grp.h> |
39 |
|
|
#include <limits.h> |
40 |
|
|
#include <paths.h> |
41 |
|
|
#include <stdarg.h> |
42 |
|
|
#include <stdio.h> |
43 |
|
|
#include <stdlib.h> |
44 |
|
|
#include <string.h> |
45 |
|
|
#include <unistd.h> |
46 |
|
|
|
47 |
|
|
#include "defs.h" |
48 |
|
|
|
49 |
|
|
/* |
50 |
|
|
* Things common to both the client and server. |
51 |
|
|
*/ |
52 |
|
|
|
53 |
|
|
/* |
54 |
|
|
* Variables common to both client and server |
55 |
|
|
*/ |
56 |
|
|
char host[HOST_NAME_MAX+1]; /* Name of this host */ |
57 |
|
|
uid_t userid = (uid_t)-1; /* User's UID */ |
58 |
|
|
gid_t groupid = (gid_t)-1; /* User's GID */ |
59 |
|
|
char *homedir = NULL; /* User's $HOME */ |
60 |
|
|
char *locuser = NULL; /* Local User's name */ |
61 |
|
|
int isserver = FALSE; /* We're the server */ |
62 |
|
|
int amchild = 0; /* This PID is a child */ |
63 |
|
|
int do_fork = 1; /* Fork child process */ |
64 |
|
|
char *currenthost = NULL; /* Current client hostname */ |
65 |
|
|
char *progname = NULL; /* Name of this program */ |
66 |
|
|
int rem_r = -1; /* Client file descriptor */ |
67 |
|
|
int rem_w = -1; /* Client file descriptor */ |
68 |
|
|
struct passwd *pw = NULL; /* Local user's pwd entry */ |
69 |
|
|
volatile sig_atomic_t contimedout = FALSE; /* Connection timed out */ |
70 |
|
|
int rtimeout = RTIMEOUT; /* Response time out */ |
71 |
|
|
jmp_buf finish_jmpbuf; /* Finish() jmp buffer */ |
72 |
|
|
int setjmp_ok = FALSE; /* setjmp()/longjmp() status */ |
73 |
|
|
char **realargv; /* Real main() argv */ |
74 |
|
|
int realargc; /* Real main() argc */ |
75 |
|
|
opt_t options = 0; /* Global install options */ |
76 |
|
|
char defowner[64] = "bin"; /* Default owner */ |
77 |
|
|
char defgroup[64] = "bin"; /* Default group */ |
78 |
|
|
|
79 |
|
|
static int sendcmdmsg(int, char *, size_t); |
80 |
|
|
static ssize_t remread(int, u_char *, size_t); |
81 |
|
|
static int remmore(void); |
82 |
|
|
|
83 |
|
|
/* |
84 |
|
|
* Front end to write() that handles partial write() requests. |
85 |
|
|
*/ |
86 |
|
|
ssize_t |
87 |
|
|
xwrite(int fd, void *buf, size_t len) |
88 |
|
|
{ |
89 |
|
|
size_t nleft = len; |
90 |
|
|
ssize_t nwritten; |
91 |
|
|
char *ptr = buf; |
92 |
|
|
|
93 |
|
|
while (nleft > 0) { |
94 |
|
|
if ((nwritten = write(fd, ptr, nleft)) <= 0) { |
95 |
|
|
return nwritten; |
96 |
|
|
} |
97 |
|
|
nleft -= nwritten; |
98 |
|
|
ptr += nwritten; |
99 |
|
|
} |
100 |
|
|
|
101 |
|
|
return len; |
102 |
|
|
} |
103 |
|
|
|
104 |
|
|
/* |
105 |
|
|
* Do run-time initialization |
106 |
|
|
*/ |
107 |
|
|
int |
108 |
|
|
init(int argc, char **argv, char **envp) |
109 |
|
|
{ |
110 |
|
|
int i; |
111 |
|
|
|
112 |
|
|
/* |
113 |
|
|
* Save a copy of our argc and argv before setargs() overwrites them |
114 |
|
|
*/ |
115 |
|
|
realargc = argc; |
116 |
|
|
realargv = xmalloc(sizeof(char *) * (argc+1)); |
117 |
|
|
for (i = 0; i < argc; i++) |
118 |
|
|
realargv[i] = xstrdup(argv[i]); |
119 |
|
|
|
120 |
|
|
pw = getpwuid(userid = getuid()); |
121 |
|
|
if (pw == NULL) { |
122 |
|
|
error("Your user id (%u) is not known to this system.", |
123 |
|
|
getuid()); |
124 |
|
|
return(-1); |
125 |
|
|
} |
126 |
|
|
|
127 |
|
|
debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n", |
128 |
|
|
userid, pw->pw_name, pw->pw_dir); |
129 |
|
|
homedir = xstrdup(pw->pw_dir); |
130 |
|
|
locuser = xstrdup(pw->pw_name); |
131 |
|
|
groupid = pw->pw_gid; |
132 |
|
|
gethostname(host, sizeof(host)); |
133 |
|
|
#if 0 |
134 |
|
|
if ((cp = strchr(host, '.')) != NULL) |
135 |
|
|
*cp = CNULL; |
136 |
|
|
#endif |
137 |
|
|
|
138 |
|
|
/* |
139 |
|
|
* If we're not root, disable paranoid ownership checks |
140 |
|
|
* since normal users cannot chown() files. |
141 |
|
|
*/ |
142 |
|
|
if (!isserver && userid != 0) { |
143 |
|
|
FLAG_ON(options, DO_NOCHKOWNER); |
144 |
|
|
FLAG_ON(options, DO_NOCHKGROUP); |
145 |
|
|
} |
146 |
|
|
|
147 |
|
|
return(0); |
148 |
|
|
} |
149 |
|
|
|
150 |
|
|
/* |
151 |
|
|
* Finish things up before ending. |
152 |
|
|
*/ |
153 |
|
|
void |
154 |
|
|
finish(void) |
155 |
|
|
{ |
156 |
|
|
debugmsg(DM_CALL, |
157 |
|
|
"finish() called: do_fork = %d amchild = %d isserver = %d", |
158 |
|
|
do_fork, amchild, isserver); |
159 |
|
|
cleanup(0); |
160 |
|
|
|
161 |
|
|
/* |
162 |
|
|
* There's no valid finish_jmpbuf for the rdist master parent. |
163 |
|
|
*/ |
164 |
|
|
if (!do_fork || amchild || isserver) { |
165 |
|
|
|
166 |
|
|
if (!setjmp_ok) { |
167 |
|
|
#ifdef DEBUG_SETJMP |
168 |
|
|
error("attemping longjmp() without target"); |
169 |
|
|
abort(); |
170 |
|
|
#else |
171 |
|
|
exit(1); |
172 |
|
|
#endif |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
longjmp(finish_jmpbuf, 1); |
176 |
|
|
/*NOTREACHED*/ |
177 |
|
|
error("Unexpected failure of longjmp() in finish()"); |
178 |
|
|
exit(2); |
179 |
|
|
} else |
180 |
|
|
exit(1); |
181 |
|
|
} |
182 |
|
|
|
183 |
|
|
/* |
184 |
|
|
* Handle lost connections |
185 |
|
|
*/ |
186 |
|
|
void |
187 |
|
|
lostconn(void) |
188 |
|
|
{ |
189 |
|
|
/* Prevent looping */ |
190 |
|
|
(void) signal(SIGPIPE, SIG_IGN); |
191 |
|
|
|
192 |
|
|
rem_r = rem_w = -1; /* Ensure we don't try to send to server */ |
193 |
|
|
checkhostname(); |
194 |
|
|
error("Lost connection to %s", |
195 |
|
|
(currenthost) ? currenthost : "(unknown)"); |
196 |
|
|
|
197 |
|
|
finish(); |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
/* |
201 |
|
|
* General signal handler |
202 |
|
|
*/ |
203 |
|
|
void |
204 |
|
|
sighandler(int sig) |
205 |
|
|
{ |
206 |
|
|
int save_errno = errno; |
207 |
|
|
|
208 |
|
|
/* XXX signal race */ |
209 |
|
|
debugmsg(DM_CALL, "sighandler() received signal %d\n", sig); |
210 |
|
|
|
211 |
|
|
switch (sig) { |
212 |
|
|
case SIGALRM: |
213 |
|
|
contimedout = TRUE; |
214 |
|
|
/* XXX signal race */ |
215 |
|
|
checkhostname(); |
216 |
|
|
error("Response time out"); |
217 |
|
|
finish(); |
218 |
|
|
break; |
219 |
|
|
|
220 |
|
|
case SIGPIPE: |
221 |
|
|
/* XXX signal race */ |
222 |
|
|
lostconn(); |
223 |
|
|
break; |
224 |
|
|
|
225 |
|
|
case SIGFPE: |
226 |
|
|
debug = !debug; |
227 |
|
|
break; |
228 |
|
|
|
229 |
|
|
case SIGHUP: |
230 |
|
|
case SIGINT: |
231 |
|
|
case SIGQUIT: |
232 |
|
|
case SIGTERM: |
233 |
|
|
/* XXX signal race */ |
234 |
|
|
finish(); |
235 |
|
|
break; |
236 |
|
|
|
237 |
|
|
default: |
238 |
|
|
/* XXX signal race */ |
239 |
|
|
fatalerr("No signal handler defined for signal %d.", sig); |
240 |
|
|
} |
241 |
|
|
errno = save_errno; |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
/* |
245 |
|
|
* Function to actually send the command char and message to the |
246 |
|
|
* remote host. |
247 |
|
|
*/ |
248 |
|
|
static int |
249 |
|
|
sendcmdmsg(int cmd, char *msg, size_t msgsize) |
250 |
|
|
{ |
251 |
|
|
int len; |
252 |
|
|
|
253 |
|
|
if (rem_w < 0) |
254 |
|
|
return(-1); |
255 |
|
|
|
256 |
|
|
/* |
257 |
|
|
* All commands except C_NONE should have a newline |
258 |
|
|
*/ |
259 |
|
|
if (cmd != C_NONE && !strchr(msg + 1, '\n')) |
260 |
|
|
(void) strlcat(msg + 1, "\n", msgsize - 1); |
261 |
|
|
|
262 |
|
|
if (cmd == C_NONE) |
263 |
|
|
len = strlen(msg); |
264 |
|
|
else { |
265 |
|
|
len = strlen(msg + 1) + 1; |
266 |
|
|
msg[0] = cmd; |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"", |
270 |
|
|
cmd, cmd, |
271 |
|
|
(cmd == C_NONE) ? len-1 : len-2, |
272 |
|
|
(cmd == C_NONE) ? msg : msg + 1); |
273 |
|
|
|
274 |
|
|
return(!(xwrite(rem_w, msg, len) == len)); |
275 |
|
|
} |
276 |
|
|
|
277 |
|
|
/* |
278 |
|
|
* Send a command message to the remote host. |
279 |
|
|
* Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...) |
280 |
|
|
* The fmt may be NULL, in which case there are no args. |
281 |
|
|
*/ |
282 |
|
|
int |
283 |
|
|
sendcmd(char cmd, const char *fmt, ...) |
284 |
|
|
{ |
285 |
|
|
static char buf[BUFSIZ]; |
286 |
|
|
va_list args; |
287 |
|
|
|
288 |
|
|
va_start(args, fmt); |
289 |
|
|
if (fmt) |
290 |
|
|
(void) vsnprintf(buf + (cmd != C_NONE), |
291 |
|
|
sizeof(buf) - (cmd != C_NONE), fmt, args); |
292 |
|
|
else |
293 |
|
|
buf[1] = CNULL; |
294 |
|
|
va_end(args); |
295 |
|
|
|
296 |
|
|
return(sendcmdmsg(cmd, buf, sizeof(buf))); |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
/* |
300 |
|
|
* Internal variables and routines for reading lines from the remote. |
301 |
|
|
*/ |
302 |
|
|
static u_char rembuf[BUFSIZ]; |
303 |
|
|
static u_char *remptr; |
304 |
|
|
static ssize_t remleft; |
305 |
|
|
|
306 |
|
|
#define remc() (--remleft < 0 ? remmore() : *remptr++) |
307 |
|
|
|
308 |
|
|
/* |
309 |
|
|
* Back end to remote read() |
310 |
|
|
*/ |
311 |
|
|
static ssize_t |
312 |
|
|
remread(int fd, u_char *buf, size_t bufsiz) |
313 |
|
|
{ |
314 |
|
|
return(read(fd, (char *)buf, bufsiz)); |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
static int |
318 |
|
|
remmore(void) |
319 |
|
|
{ |
320 |
|
|
(void) signal(SIGALRM, sighandler); |
321 |
|
|
(void) alarm(rtimeout); |
322 |
|
|
|
323 |
|
|
remleft = remread(rem_r, rembuf, sizeof(rembuf)); |
324 |
|
|
|
325 |
|
|
(void) alarm(0); |
326 |
|
|
|
327 |
|
|
if (remleft < 0) |
328 |
|
|
return (-2); /* error */ |
329 |
|
|
if (remleft == 0) |
330 |
|
|
return (-1); /* EOF */ |
331 |
|
|
remptr = rembuf; |
332 |
|
|
remleft--; |
333 |
|
|
return (*remptr++); |
334 |
|
|
} |
335 |
|
|
|
336 |
|
|
/* |
337 |
|
|
* Read an input line from the remote. Return the number of bytes |
338 |
|
|
* stored (equivalent to strlen(p)). If `cleanup' is set, EOF at |
339 |
|
|
* the beginning of a line is returned as EOF (-1); other EOFs, or |
340 |
|
|
* errors, call cleanup() or lostconn(). In other words, unless |
341 |
|
|
* the third argument is nonzero, this routine never returns failure. |
342 |
|
|
*/ |
343 |
|
|
int |
344 |
|
|
remline(u_char *buffer, int space, int doclean) |
345 |
|
|
{ |
346 |
|
|
int c, left = space; |
347 |
|
|
u_char *p = buffer; |
348 |
|
|
|
349 |
|
|
if (rem_r < 0) { |
350 |
|
|
error("Cannot read remote input: Remote descriptor not open."); |
351 |
|
|
return(-1); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
while (left > 0) { |
355 |
|
|
if ((c = remc()) < -1) { /* error */ |
356 |
|
|
if (doclean) { |
357 |
|
|
finish(); |
358 |
|
|
/*NOTREACHED*/ |
359 |
|
|
} |
360 |
|
|
lostconn(); |
361 |
|
|
/*NOTREACHED*/ |
362 |
|
|
} |
363 |
|
|
if (c == -1) { /* got EOF */ |
364 |
|
|
if (doclean) { |
365 |
|
|
if (left == space) |
366 |
|
|
return (-1);/* signal proper EOF */ |
367 |
|
|
finish(); /* improper EOF */ |
368 |
|
|
/*NOTREACHED*/ |
369 |
|
|
} |
370 |
|
|
lostconn(); |
371 |
|
|
/*NOTREACHED*/ |
372 |
|
|
} |
373 |
|
|
if (c == '\n') { |
374 |
|
|
*p = CNULL; |
375 |
|
|
|
376 |
|
|
if (debug) { |
377 |
|
|
static char mbuf[BUFSIZ]; |
378 |
|
|
|
379 |
|
|
(void) snprintf(mbuf, sizeof(mbuf), |
380 |
|
|
"<<< Cmd = %c (\\%3.3o) Msg = \"%s\"", |
381 |
|
|
buffer[0], buffer[0], |
382 |
|
|
buffer + 1); |
383 |
|
|
|
384 |
|
|
debugmsg(DM_PROTO, "%s", mbuf); |
385 |
|
|
} |
386 |
|
|
|
387 |
|
|
return (space - left); |
388 |
|
|
} |
389 |
|
|
*p++ = c; |
390 |
|
|
left--; |
391 |
|
|
} |
392 |
|
|
|
393 |
|
|
/* this will probably blow the entire session */ |
394 |
|
|
error("remote input line too long"); |
395 |
|
|
p[-1] = CNULL; /* truncate */ |
396 |
|
|
return (space); |
397 |
|
|
} |
398 |
|
|
|
399 |
|
|
/* |
400 |
|
|
* Non-line-oriented remote read. |
401 |
|
|
*/ |
402 |
|
|
ssize_t |
403 |
|
|
readrem(char *p, ssize_t space) |
404 |
|
|
{ |
405 |
|
|
if (remleft <= 0) { |
406 |
|
|
/* |
407 |
|
|
* Set remote time out alarm. |
408 |
|
|
*/ |
409 |
|
|
(void) signal(SIGALRM, sighandler); |
410 |
|
|
(void) alarm(rtimeout); |
411 |
|
|
|
412 |
|
|
remleft = remread(rem_r, rembuf, sizeof(rembuf)); |
413 |
|
|
|
414 |
|
|
(void) alarm(0); |
415 |
|
|
remptr = rembuf; |
416 |
|
|
} |
417 |
|
|
|
418 |
|
|
if (remleft <= 0) |
419 |
|
|
return (remleft); |
420 |
|
|
if (remleft < space) |
421 |
|
|
space = remleft; |
422 |
|
|
|
423 |
|
|
memcpy(p, remptr, space); |
424 |
|
|
|
425 |
|
|
remptr += space; |
426 |
|
|
remleft -= space; |
427 |
|
|
|
428 |
|
|
return (space); |
429 |
|
|
} |
430 |
|
|
|
431 |
|
|
/* |
432 |
|
|
* Get the user name for the uid. |
433 |
|
|
*/ |
434 |
|
|
char * |
435 |
|
|
getusername(uid_t uid, char *file, opt_t opts) |
436 |
|
|
{ |
437 |
|
|
static char buf[100]; |
438 |
|
|
static uid_t lastuid = (uid_t)-1; |
439 |
|
|
struct passwd *pwd = NULL; |
440 |
|
|
|
441 |
|
|
/* |
442 |
|
|
* The value of opts may have changed so we always |
443 |
|
|
* do the opts check. |
444 |
|
|
*/ |
445 |
|
|
if (IS_ON(opts, DO_NUMCHKOWNER)) { |
446 |
|
|
(void) snprintf(buf, sizeof(buf), ":%u", uid); |
447 |
|
|
return(buf); |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
/* |
451 |
|
|
* Try to avoid getpwuid() call. |
452 |
|
|
*/ |
453 |
|
|
if (lastuid == uid && buf[0] != '\0' && buf[0] != ':') |
454 |
|
|
return(buf); |
455 |
|
|
|
456 |
|
|
lastuid = uid; |
457 |
|
|
|
458 |
|
|
if ((pwd = getpwuid(uid)) == NULL) { |
459 |
|
|
if (IS_ON(opts, DO_DEFOWNER) && !isserver) |
460 |
|
|
(void) strlcpy(buf, defowner, sizeof(buf)); |
461 |
|
|
else { |
462 |
|
|
message(MT_WARNING, |
463 |
|
|
"%s: No password entry for uid %u", file, uid); |
464 |
|
|
(void) snprintf(buf, sizeof(buf), ":%u", uid); |
465 |
|
|
} |
466 |
|
|
} else { |
467 |
|
|
(void) strlcpy(buf, pwd->pw_name, sizeof(buf)); |
468 |
|
|
} |
469 |
|
|
|
470 |
|
|
return(buf); |
471 |
|
|
} |
472 |
|
|
|
473 |
|
|
/* |
474 |
|
|
* Get the group name for the gid. |
475 |
|
|
*/ |
476 |
|
|
char * |
477 |
|
|
getgroupname(gid_t gid, char *file, opt_t opts) |
478 |
|
|
{ |
479 |
|
|
static char buf[100]; |
480 |
|
|
static gid_t lastgid = (gid_t)-1; |
481 |
|
|
struct group *grp = NULL; |
482 |
|
|
|
483 |
|
|
/* |
484 |
|
|
* The value of opts may have changed so we always |
485 |
|
|
* do the opts check. |
486 |
|
|
*/ |
487 |
|
|
if (IS_ON(opts, DO_NUMCHKGROUP)) { |
488 |
|
|
(void) snprintf(buf, sizeof(buf), ":%u", gid); |
489 |
|
|
return(buf); |
490 |
|
|
} |
491 |
|
|
|
492 |
|
|
/* |
493 |
|
|
* Try to avoid getgrgid() call. |
494 |
|
|
*/ |
495 |
|
|
if (lastgid == gid && buf[0] != '\0' && buf[0] != ':') |
496 |
|
|
return(buf); |
497 |
|
|
|
498 |
|
|
lastgid = gid; |
499 |
|
|
|
500 |
|
|
if ((grp = (struct group *)getgrgid(gid)) == NULL) { |
501 |
|
|
if (IS_ON(opts, DO_DEFGROUP) && !isserver) |
502 |
|
|
(void) strlcpy(buf, defgroup, sizeof(buf)); |
503 |
|
|
else { |
504 |
|
|
message(MT_WARNING, "%s: No name for group %u", |
505 |
|
|
file, gid); |
506 |
|
|
(void) snprintf(buf, sizeof(buf), ":%u", gid); |
507 |
|
|
} |
508 |
|
|
} else |
509 |
|
|
(void) strlcpy(buf, grp->gr_name, sizeof(buf)); |
510 |
|
|
|
511 |
|
|
return(buf); |
512 |
|
|
} |
513 |
|
|
|
514 |
|
|
/* |
515 |
|
|
* Read a response from the remote host. |
516 |
|
|
*/ |
517 |
|
|
int |
518 |
|
|
response(void) |
519 |
|
|
{ |
520 |
|
|
static u_char resp[BUFSIZ]; |
521 |
|
|
u_char *s; |
522 |
|
|
int n; |
523 |
|
|
|
524 |
|
|
debugmsg(DM_CALL, "response() start\n"); |
525 |
|
|
|
526 |
|
|
n = remline(s = resp, sizeof(resp), 0); |
527 |
|
|
|
528 |
|
|
n--; |
529 |
|
|
switch (*s++) { |
530 |
|
|
case C_ACK: |
531 |
|
|
debugmsg(DM_PROTO, "received ACK\n"); |
532 |
|
|
return(0); |
533 |
|
|
case C_LOGMSG: |
534 |
|
|
if (n > 0) { |
535 |
|
|
message(MT_CHANGE, "%s", s); |
536 |
|
|
return(1); |
537 |
|
|
} |
538 |
|
|
debugmsg(DM_PROTO, "received EMPTY logmsg\n"); |
539 |
|
|
return(0); |
540 |
|
|
case C_NOTEMSG: |
541 |
|
|
if (s) |
542 |
|
|
message(MT_NOTICE, "%s", s); |
543 |
|
|
return(response()); |
544 |
|
|
|
545 |
|
|
default: |
546 |
|
|
s--; |
547 |
|
|
n++; |
548 |
|
|
/* fall into... */ |
549 |
|
|
|
550 |
|
|
case C_ERRMSG: /* Normal error message */ |
551 |
|
|
if (s) |
552 |
|
|
message(MT_NERROR, "%s", s); |
553 |
|
|
return(-1); |
554 |
|
|
|
555 |
|
|
case C_FERRMSG: /* Fatal error message */ |
556 |
|
|
if (s) |
557 |
|
|
message(MT_FERROR, "%s", s); |
558 |
|
|
finish(); |
559 |
|
|
return(-1); |
560 |
|
|
} |
561 |
|
|
/*NOTREACHED*/ |
562 |
|
|
} |
563 |
|
|
|
564 |
|
|
/* |
565 |
|
|
* This should be in expand.c but the other routines call other modules |
566 |
|
|
* that we don't want to load in. |
567 |
|
|
* |
568 |
|
|
* Expand file names beginning with `~' into the |
569 |
|
|
* user's home directory path name. Return a pointer in buf to the |
570 |
|
|
* part corresponding to `file'. |
571 |
|
|
*/ |
572 |
|
|
char * |
573 |
|
|
exptilde(char *ebuf, char *file, size_t ebufsize) |
574 |
|
|
{ |
575 |
|
|
char *pw_dir, *rest; |
576 |
|
|
size_t len; |
577 |
|
|
|
578 |
|
|
if (*file != '~') { |
579 |
|
|
notilde: |
580 |
|
|
(void) strlcpy(ebuf, file, ebufsize); |
581 |
|
|
return(ebuf); |
582 |
|
|
} |
583 |
|
|
if (*++file == CNULL) { |
584 |
|
|
pw_dir = homedir; |
585 |
|
|
rest = NULL; |
586 |
|
|
} else if (*file == '/') { |
587 |
|
|
pw_dir = homedir; |
588 |
|
|
rest = file; |
589 |
|
|
} else { |
590 |
|
|
rest = file; |
591 |
|
|
while (*rest && *rest != '/') |
592 |
|
|
rest++; |
593 |
|
|
if (*rest == '/') |
594 |
|
|
*rest = CNULL; |
595 |
|
|
else |
596 |
|
|
rest = NULL; |
597 |
|
|
if (pw == NULL || strcmp(pw->pw_name, file) != 0) { |
598 |
|
|
if ((pw = getpwnam(file)) == NULL) { |
599 |
|
|
error("%s: unknown user name", file); |
600 |
|
|
if (rest != NULL) |
601 |
|
|
*rest = '/'; |
602 |
|
|
return(NULL); |
603 |
|
|
} |
604 |
|
|
} |
605 |
|
|
if (rest != NULL) |
606 |
|
|
*rest = '/'; |
607 |
|
|
pw_dir = pw->pw_dir; |
608 |
|
|
} |
609 |
|
|
if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize) |
610 |
|
|
goto notilde; |
611 |
|
|
pw_dir = ebuf + len; |
612 |
|
|
if (rest != NULL) { |
613 |
|
|
pw_dir++; |
614 |
|
|
if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize) |
615 |
|
|
goto notilde; |
616 |
|
|
} |
617 |
|
|
return(pw_dir); |
618 |
|
|
} |
619 |
|
|
|
620 |
|
|
|
621 |
|
|
|
622 |
|
|
/* |
623 |
|
|
* Set access and modify times of a given file |
624 |
|
|
*/ |
625 |
|
|
int |
626 |
|
|
setfiletime(char *file, time_t atime, time_t mtime) |
627 |
|
|
{ |
628 |
|
|
struct timeval tv[2]; |
629 |
|
|
|
630 |
|
|
if (atime != 0 && mtime != 0) { |
631 |
|
|
tv[0].tv_sec = atime; |
632 |
|
|
tv[1].tv_sec = mtime; |
633 |
|
|
tv[0].tv_usec = tv[1].tv_usec = 0; |
634 |
|
|
return (utimes(file, tv)); |
635 |
|
|
} else /* Set to current time */ |
636 |
|
|
return (utimes(file, NULL)); |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
/* |
640 |
|
|
* Get version info |
641 |
|
|
*/ |
642 |
|
|
char * |
643 |
|
|
getversion(void) |
644 |
|
|
{ |
645 |
|
|
static char buff[BUFSIZ]; |
646 |
|
|
|
647 |
|
|
(void) snprintf(buff, sizeof(buff), |
648 |
|
|
"Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d", |
649 |
|
|
DISTVERSION, PATCHLEVEL, DISTSTATUS, |
650 |
|
|
VERSION, DISTVERSION, PATCHLEVEL); |
651 |
|
|
|
652 |
|
|
return(buff); |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
/* |
656 |
|
|
* Execute a shell command to handle special cases. |
657 |
|
|
* This is now common to both server and client |
658 |
|
|
*/ |
659 |
|
|
void |
660 |
|
|
runcommand(char *cmd) |
661 |
|
|
{ |
662 |
|
|
ssize_t nread; |
663 |
|
|
pid_t pid, wpid; |
664 |
|
|
char *cp, *s; |
665 |
|
|
char sbuf[BUFSIZ], buf[BUFSIZ]; |
666 |
|
|
int fd[2], status; |
667 |
|
|
|
668 |
|
|
if (pipe(fd) < 0) { |
669 |
|
|
error("pipe of %s failed: %s", cmd, SYSERR); |
670 |
|
|
return; |
671 |
|
|
} |
672 |
|
|
|
673 |
|
|
if ((pid = fork()) == 0) { |
674 |
|
|
/* |
675 |
|
|
* Return everything the shell commands print. |
676 |
|
|
*/ |
677 |
|
|
(void) close(0); |
678 |
|
|
(void) close(1); |
679 |
|
|
(void) close(2); |
680 |
|
|
(void) open(_PATH_DEVNULL, O_RDONLY); |
681 |
|
|
(void) dup(fd[PIPE_WRITE]); |
682 |
|
|
(void) dup(fd[PIPE_WRITE]); |
683 |
|
|
(void) close(fd[PIPE_READ]); |
684 |
|
|
(void) close(fd[PIPE_WRITE]); |
685 |
|
|
(void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL); |
686 |
|
|
_exit(127); |
687 |
|
|
} |
688 |
|
|
(void) close(fd[PIPE_WRITE]); |
689 |
|
|
s = sbuf; |
690 |
|
|
*s++ = C_LOGMSG; |
691 |
|
|
while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) { |
692 |
|
|
cp = buf; |
693 |
|
|
do { |
694 |
|
|
*s++ = *cp++; |
695 |
|
|
if (cp[-1] != '\n') { |
696 |
|
|
if (s < (char *) &sbuf[sizeof(sbuf)-1]) |
697 |
|
|
continue; |
698 |
|
|
*s++ = '\n'; |
699 |
|
|
} |
700 |
|
|
/* |
701 |
|
|
* Throw away blank lines. |
702 |
|
|
*/ |
703 |
|
|
if (s == &sbuf[2]) { |
704 |
|
|
s--; |
705 |
|
|
continue; |
706 |
|
|
} |
707 |
|
|
if (isserver) |
708 |
|
|
(void) xwrite(rem_w, sbuf, s - sbuf); |
709 |
|
|
else { |
710 |
|
|
*s = CNULL; |
711 |
|
|
message(MT_INFO, "%s", sbuf+1); |
712 |
|
|
} |
713 |
|
|
s = &sbuf[1]; |
714 |
|
|
} while (--nread); |
715 |
|
|
} |
716 |
|
|
if (s > (char *) &sbuf[1]) { |
717 |
|
|
*s++ = '\n'; |
718 |
|
|
if (isserver) |
719 |
|
|
(void) xwrite(rem_w, sbuf, s - sbuf); |
720 |
|
|
else { |
721 |
|
|
*s = CNULL; |
722 |
|
|
message(MT_INFO, "%s", sbuf+1); |
723 |
|
|
} |
724 |
|
|
} |
725 |
|
|
while ((wpid = wait(&status)) != pid && wpid != -1) |
726 |
|
|
; |
727 |
|
|
if (wpid == -1) |
728 |
|
|
status = -1; |
729 |
|
|
(void) close(fd[PIPE_READ]); |
730 |
|
|
if (status) |
731 |
|
|
error("shell returned %d", status); |
732 |
|
|
else if (isserver) |
733 |
|
|
ack(); |
734 |
|
|
} |
735 |
|
|
|
736 |
|
|
/* |
737 |
|
|
* Malloc with error checking |
738 |
|
|
*/ |
739 |
|
|
void * |
740 |
|
|
xmalloc(size_t amt) |
741 |
|
|
{ |
742 |
|
|
void *ptr; |
743 |
|
|
|
744 |
|
|
if ((ptr = malloc(amt)) == NULL) |
745 |
|
|
fatalerr("Cannot malloc %zu bytes of memory.", amt); |
746 |
|
|
|
747 |
|
|
return (ptr); |
748 |
|
|
} |
749 |
|
|
|
750 |
|
|
/* |
751 |
|
|
* realloc with error checking |
752 |
|
|
*/ |
753 |
|
|
void * |
754 |
|
|
xrealloc(void *baseptr, size_t amt) |
755 |
|
|
{ |
756 |
|
|
void *new; |
757 |
|
|
|
758 |
|
|
if ((new = realloc(baseptr, amt)) == NULL) |
759 |
|
|
fatalerr("Cannot realloc %zu bytes of memory.", amt); |
760 |
|
|
|
761 |
|
|
return (new); |
762 |
|
|
} |
763 |
|
|
|
764 |
|
|
/* |
765 |
|
|
* calloc with error checking |
766 |
|
|
*/ |
767 |
|
|
void * |
768 |
|
|
xcalloc(size_t num, size_t esize) |
769 |
|
|
{ |
770 |
|
|
void *ptr; |
771 |
|
|
|
772 |
|
|
if ((ptr = calloc(num, esize)) == NULL) |
773 |
|
|
fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.", |
774 |
|
|
num, esize, num * esize); |
775 |
|
|
|
776 |
|
|
return (ptr); |
777 |
|
|
} |
778 |
|
|
|
779 |
|
|
/* |
780 |
|
|
* Strdup with error checking |
781 |
|
|
*/ |
782 |
|
|
char * |
783 |
|
|
xstrdup(const char *str) |
784 |
|
|
{ |
785 |
|
|
size_t len = strlen(str) + 1; |
786 |
|
|
char *nstr = xmalloc(len); |
787 |
|
|
|
788 |
|
|
return (memcpy(nstr, str, len)); |
789 |
|
|
} |
790 |
|
|
|
791 |
|
|
/* |
792 |
|
|
* Private version of basename() |
793 |
|
|
*/ |
794 |
|
|
char * |
795 |
|
|
xbasename(char *path) |
796 |
|
|
{ |
797 |
|
|
char *cp; |
798 |
|
|
|
799 |
|
|
if ((cp = strrchr(path, '/')) != NULL) |
800 |
|
|
return(cp+1); |
801 |
|
|
else |
802 |
|
|
return(path); |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
/* |
806 |
|
|
* Take a colon (':') separated path to a file and |
807 |
|
|
* search until a component of that path is found and |
808 |
|
|
* return the found file name. |
809 |
|
|
*/ |
810 |
|
|
char * |
811 |
|
|
searchpath(char *path) |
812 |
|
|
{ |
813 |
|
|
char *file; |
814 |
|
|
char *space; |
815 |
|
|
int found; |
816 |
|
|
struct stat statbuf; |
817 |
|
|
|
818 |
|
|
for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) { |
819 |
|
|
if ((space = strchr(file, ' ')) != NULL) |
820 |
|
|
*space = CNULL; |
821 |
|
|
found = stat(file, &statbuf) == 0; |
822 |
|
|
if (space) |
823 |
|
|
*space = ' '; /* Put back what we zapped */ |
824 |
|
|
} |
825 |
|
|
return (file); |
826 |
|
|
} |