Commit | Line | Data |
---|---|---|
984263bc MD |
1 | /* |
2 | * Copyright (c) 1980, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
0f0f9bbd | 13 | * 3. Neither the name of the University nor the names of its contributors |
984263bc MD |
14 | * may be used to endorse or promote products derived from this software |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * $FreeBSD: src/lib/libcompat/4.3/rexec.c,v 1.5.8.3 2000/11/22 13:36:00 ben Exp $ | |
1de703da MD |
30 | * |
31 | * @(#)rexec.c 8.1 (Berkeley) 6/4/93 | |
984263bc MD |
32 | */ |
33 | ||
984263bc MD |
34 | #include <sys/types.h> |
35 | #include <sys/uio.h> | |
36 | #include <sys/socket.h> | |
37 | #include <sys/param.h> | |
38 | #include <sys/stat.h> | |
39 | ||
40 | #include <netinet/in.h> | |
41 | ||
42 | #include <stdio.h> | |
43 | #include <unistd.h> | |
44 | #include <string.h> | |
45 | #include <netdb.h> | |
46 | #include <errno.h> | |
47 | #include <ctype.h> | |
48 | #include <err.h> | |
49 | #include <stdlib.h> | |
984263bc | 50 | |
bd23c1cd SW |
51 | #define SA_LEN(addr) ((addr)->sa_len) |
52 | #define __set_errno(val) errno = (val) | |
53 | ||
984263bc MD |
54 | int rexecoptions; |
55 | char *getpass(), *getlogin(); | |
56 | ||
57 | /* | |
58 | * Options and other state info. | |
59 | */ | |
60 | struct macel { | |
61 | char mac_name[9]; /* macro name */ | |
62 | char *mac_start; /* start of macro in macbuf */ | |
63 | char *mac_end; /* end of macro in macbuf */ | |
64 | }; | |
65 | ||
66 | int macnum; /* number of defined macros */ | |
67 | struct macel macros[16]; | |
68 | char macbuf[4096]; | |
69 | ||
70 | static FILE *cfile; | |
71 | ||
72 | #define DEFAULT 1 | |
73 | #define LOGIN 2 | |
74 | #define PASSWD 3 | |
75 | #define ACCOUNT 4 | |
76 | #define MACDEF 5 | |
77 | #define ID 10 | |
78 | #define MACH 11 | |
79 | ||
80 | static char tokval[100]; | |
81 | ||
82 | static struct toktab { | |
83 | char *tokstr; | |
84 | int tval; | |
85 | } toktab[]= { | |
86 | { "default", DEFAULT }, | |
87 | { "login", LOGIN }, | |
88 | { "password", PASSWD }, | |
89 | { "passwd", PASSWD }, | |
90 | { "account", ACCOUNT }, | |
91 | { "machine", MACH }, | |
92 | { "macdef", MACDEF }, | |
93 | { NULL, 0 } | |
94 | }; | |
95 | ||
96 | static int | |
d797dc5d | 97 | token(void) |
984263bc MD |
98 | { |
99 | char *cp; | |
100 | int c; | |
101 | struct toktab *t; | |
102 | ||
103 | if (feof(cfile) || ferror(cfile)) | |
104 | return (0); | |
105 | while ((c = getc(cfile)) != EOF && | |
106 | (c == '\n' || c == '\t' || c == ' ' || c == ',')) | |
107 | continue; | |
108 | if (c == EOF) | |
109 | return (0); | |
110 | cp = tokval; | |
111 | if (c == '"') { | |
112 | while ((c = getc(cfile)) != EOF && c != '"') { | |
113 | if (c == '\\') | |
114 | c = getc(cfile); | |
115 | *cp++ = c; | |
116 | } | |
117 | } else { | |
118 | *cp++ = c; | |
119 | while ((c = getc(cfile)) != EOF | |
120 | && c != '\n' && c != '\t' && c != ' ' && c != ',') { | |
121 | if (c == '\\') | |
122 | c = getc(cfile); | |
123 | *cp++ = c; | |
124 | } | |
125 | } | |
126 | *cp = 0; | |
127 | if (tokval[0] == 0) | |
128 | return (0); | |
129 | for (t = toktab; t->tokstr; t++) | |
130 | if (!strcmp(t->tokstr, tokval)) | |
131 | return (t->tval); | |
132 | return (ID); | |
133 | } | |
134 | ||
135 | static int | |
a276dc6b | 136 | ruserpass(char *host, const char **aname, const char **apass, char **aacct) |
984263bc MD |
137 | { |
138 | char *hdir, buf[BUFSIZ], *tmp; | |
139 | char myname[MAXHOSTNAMELEN], *mydomain; | |
140 | int t, i, c, usedefault = 0; | |
141 | struct stat stb; | |
142 | ||
143 | hdir = getenv("HOME"); | |
144 | if (hdir == NULL) | |
145 | hdir = "."; | |
146 | if (strlen(hdir) + 8 > sizeof(buf)) | |
147 | return (0); | |
148 | (void) sprintf(buf, "%s/.netrc", hdir); | |
149 | cfile = fopen(buf, "r"); | |
150 | if (cfile == NULL) { | |
151 | if (errno != ENOENT) | |
152 | warn("%s", buf); | |
153 | return (0); | |
154 | } | |
155 | if (gethostname(myname, sizeof(myname)) < 0) | |
156 | myname[0] = '\0'; | |
157 | if ((mydomain = strchr(myname, '.')) == NULL) | |
158 | mydomain = ""; | |
159 | next: | |
160 | while ((t = token())) switch(t) { | |
161 | ||
162 | case DEFAULT: | |
163 | usedefault = 1; | |
164 | /* FALL THROUGH */ | |
165 | ||
166 | case MACH: | |
167 | if (!usedefault) { | |
168 | if (token() != ID) | |
169 | continue; | |
170 | /* | |
171 | * Allow match either for user's input host name | |
172 | * or official hostname. Also allow match of | |
173 | * incompletely-specified host in local domain. | |
174 | */ | |
175 | if (strcasecmp(host, tokval) == 0) | |
176 | goto match; | |
177 | if ((tmp = strchr(host, '.')) != NULL && | |
178 | strcasecmp(tmp, mydomain) == 0 && | |
179 | strncasecmp(host, tokval, tmp - host) == 0 && | |
180 | tokval[tmp - host] == '\0') | |
181 | goto match; | |
182 | continue; | |
183 | } | |
184 | match: | |
185 | while ((t = token()) && t != MACH && t != DEFAULT) switch(t) { | |
186 | ||
187 | case LOGIN: | |
bd23c1cd | 188 | if (token()) { |
a276dc6b MD |
189 | if (*aname == NULL) { |
190 | char *tmp; | |
191 | tmp = malloc(strlen(tokval) + 1); | |
192 | strcpy(tmp, tokval); | |
193 | *aname = tmp; | |
984263bc MD |
194 | } else { |
195 | if (strcmp(*aname, tokval)) | |
196 | goto next; | |
197 | } | |
bd23c1cd | 198 | } |
984263bc MD |
199 | break; |
200 | case PASSWD: | |
678e8cc6 | 201 | if ((*aname == NULL || strcmp(*aname, "anonymous")) && |
984263bc MD |
202 | fstat(fileno(cfile), &stb) >= 0 && |
203 | (stb.st_mode & 077) != 0) { | |
204 | warnx("Error: .netrc file is readable by others."); | |
205 | warnx("Remove password or make file unreadable by others."); | |
206 | goto bad; | |
207 | } | |
678e8cc6 | 208 | if (token() && *apass == NULL) { |
a276dc6b MD |
209 | char *tmp; |
210 | tmp = malloc(strlen(tokval) + 1); | |
211 | strcpy(tmp, tokval); | |
212 | *apass = tmp; | |
984263bc MD |
213 | } |
214 | break; | |
215 | case ACCOUNT: | |
216 | if (fstat(fileno(cfile), &stb) >= 0 | |
217 | && (stb.st_mode & 077) != 0) { | |
218 | warnx("Error: .netrc file is readable by others."); | |
219 | warnx("Remove account or make file unreadable by others."); | |
220 | goto bad; | |
221 | } | |
678e8cc6 | 222 | if (token() && *aacct == NULL) { |
984263bc MD |
223 | *aacct = malloc((unsigned) strlen(tokval) + 1); |
224 | (void) strcpy(*aacct, tokval); | |
225 | } | |
226 | break; | |
227 | case MACDEF: | |
228 | while ((c=getc(cfile)) != EOF && | |
229 | (c == ' ' || c == '\t')) | |
230 | ; | |
231 | if (c == EOF || c == '\n') { | |
232 | printf("Missing macdef name argument.\n"); | |
233 | goto bad; | |
234 | } | |
235 | if (macnum == 16) { | |
236 | printf("Limit of 16 macros have already been defined\n"); | |
237 | goto bad; | |
238 | } | |
239 | tmp = macros[macnum].mac_name; | |
240 | *tmp++ = c; | |
241 | for (i=0; i < 8 && (c=getc(cfile)) != EOF && | |
242 | !isspace(c); ++i) { | |
243 | *tmp++ = c; | |
244 | } | |
245 | if (c == EOF) { | |
246 | printf("Macro definition missing null line terminator.\n"); | |
247 | goto bad; | |
248 | } | |
249 | *tmp = '\0'; | |
250 | if (c != '\n') { | |
251 | while ((c=getc(cfile)) != EOF && c != '\n'); | |
252 | } | |
253 | if (c == EOF) { | |
254 | printf("Macro definition missing null line terminator.\n"); | |
255 | goto bad; | |
256 | } | |
257 | if (macnum == 0) { | |
258 | macros[macnum].mac_start = macbuf; | |
259 | } | |
260 | else { | |
261 | macros[macnum].mac_start = macros[macnum-1].mac_end + 1; | |
262 | } | |
263 | tmp = macros[macnum].mac_start; | |
264 | while (tmp != macbuf + 4096) { | |
265 | if ((c=getc(cfile)) == EOF) { | |
266 | printf("Macro definition missing null line terminator.\n"); | |
267 | goto bad; | |
268 | } | |
269 | *tmp = c; | |
270 | if (*tmp == '\n') { | |
271 | if (*(tmp-1) == '\0') { | |
272 | macros[macnum++].mac_end = tmp - 1; | |
273 | break; | |
274 | } | |
275 | *tmp = '\0'; | |
276 | } | |
277 | tmp++; | |
278 | } | |
279 | if (tmp == macbuf + 4096) { | |
280 | printf("4K macro buffer exceeded\n"); | |
281 | goto bad; | |
282 | } | |
283 | break; | |
284 | default: | |
285 | warnx("Unknown .netrc keyword %s", tokval); | |
286 | break; | |
287 | } | |
288 | goto done; | |
289 | } | |
290 | done: | |
291 | (void) fclose(cfile); | |
292 | return (0); | |
293 | bad: | |
294 | (void) fclose(cfile); | |
295 | return (-1); | |
296 | } | |
297 | ||
5aa41e7c | 298 | int |
d797dc5d SW |
299 | rexec_af(char **ahost, int rport, const char *name, const char *pass, |
300 | const char *cmd, int *fd2p, sa_family_t *af) | |
5aa41e7c HT |
301 | { |
302 | struct sockaddr_storage sa2, from; | |
303 | struct addrinfo hints, *res0; | |
304 | const char *orig_name = name; | |
305 | const char *orig_pass = pass; | |
306 | static char *ahostbuf; | |
307 | u_short port = 0; | |
308 | int s, timo = 1, s3; | |
309 | char c; | |
310 | int gai; | |
311 | char servbuff[NI_MAXSERV]; | |
312 | ||
67516b71 | 313 | snprintf(servbuff, sizeof(servbuff), "%d", ntohs(rport)); |
5aa41e7c HT |
314 | servbuff[sizeof(servbuff) - 1] = '\0'; |
315 | ||
316 | memset(&hints, '\0', sizeof(hints)); | |
a276dc6b MD |
317 | if (af) |
318 | hints.ai_family = *af; | |
5aa41e7c HT |
319 | hints.ai_socktype = SOCK_STREAM; |
320 | hints.ai_flags = AI_CANONNAME; | |
321 | gai = getaddrinfo(*ahost, servbuff, &hints, &res0); | |
322 | if (gai){ | |
323 | /* XXX: set errno? */ | |
324 | return -1; | |
325 | } | |
326 | ||
327 | if (res0->ai_canonname){ | |
328 | free (ahostbuf); | |
329 | ahostbuf = strdup (res0->ai_canonname); | |
330 | if (ahostbuf == NULL) { | |
331 | perror ("rexec: strdup"); | |
332 | return (-1); | |
333 | } | |
334 | *ahost = ahostbuf; | |
335 | } else { | |
336 | *ahost = NULL; | |
337 | __set_errno (ENOENT); | |
338 | return -1; | |
339 | } | |
d797dc5d | 340 | ruserpass(res0->ai_canonname, &name, &pass, 0); |
5aa41e7c HT |
341 | retry: |
342 | s = socket(res0->ai_family, res0->ai_socktype, 0); | |
343 | if (s < 0) { | |
344 | perror("rexec: socket"); | |
345 | return (-1); | |
346 | } | |
347 | if (connect(s, res0->ai_addr, res0->ai_addrlen) < 0) { | |
348 | if (errno == ECONNREFUSED && timo <= 16) { | |
349 | (void) close(s); | |
350 | sleep(timo); | |
351 | timo *= 2; | |
352 | goto retry; | |
353 | } | |
354 | perror(res0->ai_canonname); | |
355 | return (-1); | |
356 | } | |
678e8cc6 | 357 | if (fd2p == NULL) { |
5aa41e7c HT |
358 | (void) write(s, "", 1); |
359 | port = 0; | |
360 | } else { | |
361 | char num[32]; | |
362 | int s2; | |
363 | socklen_t sa2len; | |
364 | ||
365 | s2 = socket(res0->ai_family, res0->ai_socktype, 0); | |
366 | if (s2 < 0) { | |
367 | (void) close(s); | |
368 | return (-1); | |
369 | } | |
370 | listen(s2, 1); | |
371 | sa2len = sizeof (sa2); | |
372 | if (getsockname(s2, (struct sockaddr *)&sa2, &sa2len) < 0) { | |
373 | perror("getsockname"); | |
374 | (void) close(s2); | |
375 | goto bad; | |
376 | } else if (sa2len != SA_LEN((struct sockaddr *)&sa2)) { | |
377 | __set_errno(EINVAL); | |
378 | (void) close(s2); | |
379 | goto bad; | |
380 | } | |
381 | port = 0; | |
382 | if (!getnameinfo((struct sockaddr *)&sa2, sa2len, | |
383 | NULL, 0, servbuff, sizeof(servbuff), | |
384 | NI_NUMERICSERV)) | |
385 | port = atoi(servbuff); | |
386 | (void) sprintf(num, "%u", port); | |
387 | (void) write(s, num, strlen(num)+1); | |
388 | { socklen_t len = sizeof (from); | |
389 | s3 = accept(s2, (struct sockaddr *)&from, | |
390 | &len); | |
391 | close(s2); | |
392 | if (s3 < 0) { | |
393 | perror("accept"); | |
394 | port = 0; | |
395 | goto bad; | |
396 | } | |
397 | } | |
398 | *fd2p = s3; | |
399 | } | |
400 | ||
401 | (void) write(s, name, strlen(name) + 1); | |
402 | /* should public key encypt the password here */ | |
403 | (void) write(s, pass, strlen(pass) + 1); | |
404 | (void) write(s, cmd, strlen(cmd) + 1); | |
405 | ||
406 | /* We don't need the memory allocated for the name and the password | |
407 | in ruserpass anymore. */ | |
408 | if (name != orig_name) | |
409 | free ((char *) name); | |
410 | if (pass != orig_pass) | |
411 | free ((char *) pass); | |
412 | ||
413 | if (read(s, &c, 1) != 1) { | |
414 | perror(*ahost); | |
415 | goto bad; | |
416 | } | |
417 | if (c != 0) { | |
418 | while (read(s, &c, 1) == 1) { | |
419 | (void) write(2, &c, 1); | |
420 | if (c == '\n') | |
421 | break; | |
422 | } | |
423 | goto bad; | |
424 | } | |
425 | freeaddrinfo(res0); | |
426 | return (s); | |
427 | bad: | |
428 | if (port) | |
429 | (void) close(*fd2p); | |
430 | (void) close(s); | |
431 | freeaddrinfo(res0); | |
432 | return (-1); | |
433 | } | |
434 | ||
435 | ||
984263bc | 436 | int |
a276dc6b | 437 | rexec(char **ahost, int rport, const char *name, const char *pass, char *cmd, int *fd2p) |
984263bc MD |
438 | { |
439 | struct sockaddr_in sin, sin2, from; | |
440 | struct hostent *hp; | |
441 | u_short port; | |
442 | int s, timo = 1, s3; | |
443 | char c; | |
bdddf849 | 444 | char *acct = NULL; |
984263bc MD |
445 | |
446 | hp = gethostbyname(*ahost); | |
678e8cc6 | 447 | if (hp == NULL) { |
984263bc MD |
448 | herror(*ahost); |
449 | return (-1); | |
450 | } | |
451 | *ahost = hp->h_name; | |
bdddf849 SW |
452 | ruserpass(hp->h_name, &name, &pass, &acct); |
453 | if (acct != NULL) | |
454 | free(acct); | |
984263bc MD |
455 | retry: |
456 | s = socket(AF_INET, SOCK_STREAM, 0); | |
457 | if (s < 0) { | |
458 | perror("rexec: socket"); | |
459 | return (-1); | |
460 | } | |
461 | sin.sin_family = hp->h_addrtype; | |
462 | sin.sin_port = rport; | |
463 | bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length); | |
464 | if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) { | |
465 | if (errno == ECONNREFUSED && timo <= 16) { | |
466 | (void) close(s); | |
467 | sleep(timo); | |
468 | timo *= 2; | |
469 | goto retry; | |
470 | } | |
471 | perror(hp->h_name); | |
472 | return (-1); | |
473 | } | |
678e8cc6 | 474 | if (fd2p == NULL) { |
984263bc MD |
475 | (void) write(s, "", 1); |
476 | port = 0; | |
477 | } else { | |
478 | char num[8]; | |
479 | int s2, sin2len; | |
480 | ||
481 | s2 = socket(AF_INET, SOCK_STREAM, 0); | |
482 | if (s2 < 0) { | |
483 | (void) close(s); | |
484 | return (-1); | |
485 | } | |
486 | listen(s2, 1); | |
487 | sin2len = sizeof (sin2); | |
488 | if (getsockname(s2, (struct sockaddr *)&sin2, &sin2len) < 0 || | |
489 | sin2len != sizeof (sin2)) { | |
490 | perror("getsockname"); | |
491 | (void) close(s2); | |
492 | goto bad; | |
493 | } | |
494 | port = ntohs((u_short)sin2.sin_port); | |
82e1476a | 495 | (void) sprintf(num, "%hu", port); |
984263bc MD |
496 | (void) write(s, num, strlen(num)+1); |
497 | { int len = sizeof (from); | |
498 | s3 = accept(s2, (struct sockaddr *)&from, &len); | |
499 | close(s2); | |
500 | if (s3 < 0) { | |
501 | perror("accept"); | |
502 | port = 0; | |
503 | goto bad; | |
504 | } | |
505 | } | |
506 | *fd2p = s3; | |
507 | } | |
508 | (void) write(s, name, strlen(name) + 1); | |
509 | /* should public key encypt the password here */ | |
510 | (void) write(s, pass, strlen(pass) + 1); | |
511 | (void) write(s, cmd, strlen(cmd) + 1); | |
512 | if (read(s, &c, 1) != 1) { | |
513 | perror(*ahost); | |
514 | goto bad; | |
515 | } | |
516 | if (c != 0) { | |
517 | while (read(s, &c, 1) == 1) { | |
518 | (void) write(2, &c, 1); | |
519 | if (c == '\n') | |
520 | break; | |
521 | } | |
522 | goto bad; | |
523 | } | |
524 | return (s); | |
525 | bad: | |
526 | if (port) | |
527 | (void) close(*fd2p); | |
528 | (void) close(s); | |
529 | return (-1); | |
530 | } |