/src/qsnet/qshell.c

https://code.google.com/ · C · 969 lines · 638 code · 149 blank · 182 comment · 154 complexity · 9faafc3236b7b1f2c2db1b1b18bb5f6c MD5 · raw file

  1. /*****************************************************************************\
  2. * $Id$
  3. *****************************************************************************
  4. * Copyright (C) 2001-2006 The Regents of the University of California.
  5. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  6. * Written by Jim Garlick <garlick@llnl.gov>.
  7. * UCRL-CODE-2003-005.
  8. *
  9. * This file is part of Pdsh, a parallel remote shell program.
  10. * For details, see <http://www.llnl.gov/linux/pdsh/>.
  11. *
  12. * Pdsh is free software; you can redistribute it and/or modify it under
  13. * the terms of the GNU General Public License as published by the Free
  14. * Software Foundation; either version 2 of the License, or (at your option)
  15. * any later version.
  16. *
  17. * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY
  18. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  19. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  20. * details.
  21. *
  22. * You should have received a copy of the GNU General Public License along
  23. * with Pdsh; if not, write to the Free Software Foundation, Inc.,
  24. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  25. \*****************************************************************************/
  26. /*-
  27. * Started with BSD rshd which is:
  28. *
  29. * Copyright (c) 1988, 1989 The Regents of the University of California.
  30. * All rights reserved.
  31. *
  32. * Redistribution and use in source and binary forms, with or without
  33. * modification, are permitted provided that the following conditions
  34. * are met:
  35. * 1. Redistributions of source code must retain the above copyright
  36. * notice, this list of conditions and the following disclaimer.
  37. *
  38. * 2. Redistributions in binary form must reproduce the above copyright
  39. * notice, this list of conditions and the following disclaimer in the
  40. * documentation and/or other materials provided with the distribution.
  41. *
  42. * 3. All advertising materials mentioning features or use of this software
  43. * must display the following acknowledgement:
  44. * This product includes software developed by the University of
  45. * California, Berkeley and its contributors.
  46. *
  47. * 4. Neither the name of the University nor the names of its contributors
  48. * may be used to endorse or promote products derived from this software
  49. * without specific prior written permission.
  50. *
  51. * 5. This is free software; you can redistribute it and/or modify it
  52. * under the terms of the GNU General Public License as published
  53. * by the Free Software Foundation; either version 2 of the
  54. * License, or (at your option) any later version.
  55. *
  56. * 6. This is distributed in the hope that it will be useful, but
  57. * WITHOUT ANY WARRANTY; without even the implied warranty of
  58. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  59. * GNU General Public License for more details.
  60. *
  61. * 7. You should have received a copy of the GNU General Public License;
  62. * if not, write to the Free Software Foundation, Inc., 59 Temple
  63. * Place, Suite 330, Boston, MA 02111-1307 USA.
  64. *
  65. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  66. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  67. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  68. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  69. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  70. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  71. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  72. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  73. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  74. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  75. * SUCH DAMAGE.
  76. */
  77. /*
  78. * PAM modifications by Michael K. Johnson <johnsonm@redhat.com>
  79. */
  80. #if HAVE_CONFIG_H
  81. #include "config.h"
  82. #endif
  83. #include <sys/types.h>
  84. #include <sys/param.h>
  85. #include <sys/ioctl.h>
  86. #include <sys/time.h>
  87. #include <sys/wait.h>
  88. #include <fcntl.h>
  89. #include <signal.h>
  90. #include <sys/select.h>
  91. #include <sys/socket.h>
  92. #include <netinet/in.h>
  93. #include <arpa/inet.h>
  94. #include <netdb.h>
  95. #include <pwd.h>
  96. #include <grp.h>
  97. #include <sys/syslog.h>
  98. #include <resolv.h>
  99. #if HAVE_UNISTD_H
  100. #include <unistd.h>
  101. #endif
  102. #include <errno.h>
  103. #include <stdio.h>
  104. #include <stdlib.h>
  105. #include <string.h>
  106. #include <paths.h>
  107. #include <stdarg.h>
  108. #include <ctype.h>
  109. #include <assert.h>
  110. #include "src/common/xmalloc.h"
  111. #include "src/common/xstring.h"
  112. #include "src/common/err.h"
  113. #include "src/common/fd.h"
  114. #include "src/qsnet/qswutil.h"
  115. #include "src/qsnet/qshell.h"
  116. int keepalive = 1;
  117. int check_all = 0;
  118. int paranoid = 0;
  119. int sent_null = 0;
  120. int allow_root_rhosts = 1;
  121. static char typename[BUFSIZ];
  122. #define OPTIONS "ahlLn"
  123. #if defined(__GLIBC__) && (__GLIBC__ >= 2)
  124. #define _check_rhosts_file __check_rhosts_file
  125. #endif
  126. extern int _check_rhosts_file;
  127. #ifdef USE_PAM
  128. #include <security/pam_appl.h>
  129. #include <security/pam_misc.h>
  130. #include "src/common/list.h"
  131. pam_handle_t *pamh;
  132. static char *last_pam_msg = NULL;
  133. #define PAM_ERRMSGLEN 4096
  134. char pam_errmsgbuf[PAM_ERRMSGLEN];
  135. const char *pam_errmsg = NULL;
  136. #endif
  137. char username[20] = "USER=";
  138. char homedir[64] = "HOME=";
  139. char shell[64] = "SHELL=";
  140. char path[100] = "PATH=";
  141. char *envinit[] = { homedir, shell, path, username, 0 };
  142. extern char **environ;
  143. static void errcommon(int, const char *, va_list, int);
  144. static int readchar(char *);
  145. static int getint(void);
  146. static int get_qshell_info(ELAN_CAPABILITY *, qsw_info_t *, char *, int);
  147. static void stderr_parent(int, int, int);
  148. static void network_init(int, struct sockaddr_in *, int);
  149. void errcommon(int fd, const char *fmt, va_list ap, int log) {
  150. char buf[BUFSIZ], *bp = buf;
  151. if (sent_null == 0) *bp++ = 1;
  152. vsnprintf(bp, sizeof(buf) - 1, fmt, ap);
  153. if (log)
  154. syslog(LOG_ERR, (sent_null == 0) ? buf + 1 : buf);
  155. fd_write_n(fd, buf, strlen(buf));
  156. }
  157. void error(const char *fmt, ...) {
  158. va_list ap;
  159. va_start(ap, fmt);
  160. errcommon(2, fmt, ap, 0);
  161. va_end(ap);
  162. }
  163. void errlog(const char *fmt, ...) {
  164. va_list ap;
  165. va_start(ap, fmt);
  166. errcommon(2, fmt, ap, 1);
  167. va_end(ap);
  168. }
  169. int readchar(char *err) {
  170. int rv;
  171. char c;
  172. if ((rv = read(0, &c, 1)) < 0) {
  173. errlog("%s: read %s error: %s\n", typename, err, strerror(errno));
  174. return -1;
  175. }
  176. if (rv != 1) {
  177. errlog("%s: %s read wrong number of bytes: %d\n", typename, err, rv);
  178. return -1;
  179. }
  180. return (int)c;
  181. }
  182. int getstr(char *buf, int cnt, char *err) {
  183. int rv;
  184. do {
  185. if ((rv = readchar(err)) == -1)
  186. return -1;
  187. *buf++ = (char)rv;
  188. if (--cnt == 0) {
  189. errlog("%s: %s too long\n", typename, err);
  190. return -1;
  191. }
  192. } while (rv != 0);
  193. return cnt;
  194. }
  195. int getint(void) {
  196. /* achu: Must fatally exit here without message back to user. No
  197. * port to connect on stderr, and a write on stdout calls
  198. * xrcmd/mcmd to fail in xpoll.
  199. */
  200. int port = 0;
  201. char c;
  202. do {
  203. /* achu: Can't use readchar, for above reason */
  204. if (read(0, &c, 1) != 1) {
  205. syslog(LOG_ERR, "read port: %m");
  206. exit(1);
  207. }
  208. if (isascii(c) && isdigit(c))
  209. port = port*10 + c-'0';
  210. } while (c != 0);
  211. return port;
  212. }
  213. static void copy_passwd_struct(struct passwd *to, struct passwd *from)
  214. {
  215. to->pw_uid = from->pw_uid;
  216. to->pw_gid = from->pw_gid;
  217. to->pw_name = strdup(from->pw_name);
  218. to->pw_passwd = strdup(from->pw_passwd);
  219. to->pw_gecos = strdup(from->pw_gecos);
  220. to->pw_dir = strdup(from->pw_dir);
  221. to->pw_shell = strdup(from->pw_shell);
  222. return;
  223. }
  224. /*
  225. * Get a *copy* of passwd struct for user ``user''
  226. */
  227. struct passwd *getpwnam_common(char *user) {
  228. struct passwd *pwd;
  229. struct passwd *storage;
  230. setpwent();
  231. if ((pwd= getpwnam(user)) == NULL)
  232. return NULL;
  233. if (pwd->pw_uid != 0 && !access(_PATH_NOLOGIN, F_OK))
  234. return NULL;
  235. if (pwd->pw_uid == 0 || !strcmp(user, "root"))
  236. paranoid = 1;
  237. storage = Malloc(sizeof(struct passwd));
  238. copy_passwd_struct(storage, pwd);
  239. return storage;
  240. }
  241. char *findhostname(struct sockaddr_in *fromp) {
  242. struct hostent *hp;
  243. char *hostname;
  244. char *addr = (char *)&fromp->sin_addr;
  245. int inaddrsize = sizeof(struct in_addr);
  246. if ((hp = gethostbyaddr(addr, inaddrsize, fromp->sin_family)) == NULL)
  247. hostname = strdup(inet_ntoa(fromp->sin_addr));
  248. else
  249. hostname = strdup(hp->h_name);
  250. if (hostname == NULL) {
  251. syslog(LOG_ERR, "System out of memory");
  252. return NULL;
  253. }
  254. /*
  255. * Attempt to confirm the DNS.
  256. */
  257. #ifdef RES_DNSRCH
  258. _res.options &= ~RES_DNSRCH;
  259. #endif
  260. if ((hp = gethostbyname(hostname)) == NULL) {
  261. syslog(LOG_INFO | LOG_AUTH, "Couldn't look up address for %s", hostname);
  262. return NULL;
  263. }
  264. while (hp->h_addr_list[0] != NULL) {
  265. if (!memcmp(hp->h_addr_list[0], addr, sizeof(fromp->sin_addr))) {
  266. return hostname;
  267. }
  268. hp->h_addr_list++;
  269. }
  270. syslog(LOG_NOTICE | LOG_INFO | LOG_AUTH, "Host addr %s not listed for host %s",
  271. inet_ntoa(fromp->sin_addr), hp->h_name);
  272. return NULL;
  273. }
  274. #ifdef USE_PAM
  275. int qshell_conv(int num_msg, const struct pam_message **msg,
  276. struct pam_response **resp, void *appdata_ptr) {
  277. /* rcmd/mqcmd requires that no data come over the stdout connection
  278. * before a separate stderr connection has been made. Therefore,
  279. * we cannot send PAM_ERROR_MSG or PAM_TEXT_INFO over stdout.
  280. * Instead, we will store all messages in a list until we are
  281. * assured of a PAM_FAILURE or PAM_SUCCESS.
  282. */
  283. int i = 0;
  284. struct pam_response *reply;
  285. if (num_msg <= 0)
  286. return PAM_CONV_ERR;
  287. reply = (struct pam_response *)malloc(num_msg*sizeof(struct pam_response));
  288. if (!reply)
  289. return PAM_CONV_ERR;
  290. for (i = 0; i < num_msg; i++) {
  291. char *string = NULL;
  292. switch (msg[i]->msg_style) {
  293. case PAM_ERROR_MSG:
  294. case PAM_TEXT_INFO:
  295. {
  296. char *str = NULL;
  297. List pam_msgs = *((List *)appdata_ptr);
  298. if (!(msg[i]->msg))
  299. return -1;
  300. if ((str = strdup(msg[i]->msg)) == NULL)
  301. return -1;
  302. if (list_append(pam_msgs, (void *)str) == NULL) {
  303. syslog(LOG_ERR, "list_append failed: %s\n", str);
  304. free(str);
  305. return -1;
  306. }
  307. last_pam_msg = str;
  308. break;
  309. }
  310. case PAM_BINARY_PROMPT:
  311. {
  312. /* More or less ripped from PAM's misc_conv.c */
  313. /* As of this revision, PAM 0.77 functionality of
  314. * PAM_BINARY_PROMPT was still under development. This
  315. * code is placed here to hopefully be compatible with any
  316. * existing pam modules that may use PAM_BINARY_PROMPT.
  317. */
  318. pamc_bp_t binary_prompt = NULL;
  319. if (!msg[i]->msg || !pam_binary_handler_fn)
  320. goto error;
  321. PAM_BP_RENEW(&binary_prompt,
  322. PAM_BP_RCONTROL(msg[i]->msg),
  323. PAM_BP_LENGTH(msg[i]->msg));
  324. PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msg[i]->msg),
  325. PAM_BP_RDATA(msg[i]->msg));
  326. if (pam_binary_handler_fn(appdata_ptr,
  327. &binary_prompt) != PAM_SUCCESS
  328. || (binary_prompt == NULL))
  329. goto error;
  330. string = (char *) binary_prompt;
  331. binary_prompt = NULL;
  332. break;
  333. }
  334. case PAM_PROMPT_ECHO_OFF:
  335. case PAM_PROMPT_ECHO_ON:
  336. /* Giving the user a prompt for input defeats the purpose
  337. * of not sending the data over stdout. A pam module
  338. * under mqsh should never ask the user for data. So fall
  339. * through.
  340. */
  341. default:
  342. syslog(LOG_ERR, "bad conversation: %d", msg[i]->msg_style);
  343. goto error;
  344. break;
  345. }
  346. /* Must set values, or _pam_drop_reply in pam modules will fail */
  347. reply[i].resp_retcode = 0;
  348. if (string != NULL) {
  349. reply[i].resp = string;
  350. string = NULL;
  351. }
  352. else
  353. reply[i].resp = NULL;
  354. }
  355. *resp = reply;
  356. reply = NULL;
  357. return PAM_SUCCESS;
  358. error:
  359. if (reply) {
  360. for (i = 0; i < num_msg; i++) {
  361. if (reply[i].resp == NULL)
  362. continue;
  363. if (msg[i]->msg_style == PAM_BINARY_PROMPT)
  364. pam_binary_handler_free(appdata_ptr,
  365. (pamc_bp_t *) &reply[i].resp);
  366. else
  367. /* Uhh, we shouldn't be able to get here */
  368. free(reply[i].resp);
  369. reply[i].resp = NULL;
  370. }
  371. free(reply);
  372. reply = NULL;
  373. }
  374. return PAM_CONV_ERR;
  375. }
  376. int pamauth(struct passwd *pwd, char *service, char *remuser,
  377. char *hostname, char *locuser) {
  378. static struct pam_conv conv;
  379. int retcode;
  380. List pam_msgs = NULL;
  381. if ((pam_msgs = list_create((ListDelF)free)) == NULL) {
  382. syslog(LOG_ERR, "list_create() failed\n");
  383. pam_errmsg = "Internal System Error";
  384. goto error;
  385. }
  386. conv.conv = qshell_conv;
  387. conv.appdata_ptr = (void *)&pam_msgs;
  388. retcode = pam_start(service, locuser, &conv, &pamh);
  389. if (retcode != PAM_SUCCESS) {
  390. syslog(LOG_ERR, "pam_start: %s\n", pam_strerror(pamh, retcode));
  391. goto error;
  392. }
  393. pam_set_item(pamh, PAM_RUSER, remuser);
  394. pam_set_item(pamh, PAM_RHOST, hostname);
  395. pam_set_item(pamh, PAM_TTY, service);
  396. retcode = pam_authenticate(pamh, 0);
  397. if (retcode == PAM_SUCCESS) {
  398. last_pam_msg = NULL;
  399. retcode = pam_acct_mgmt(pamh, 0);
  400. }
  401. if (retcode == PAM_SUCCESS) {
  402. /*
  403. * Why do we need to set groups here?
  404. * Also, this stuff should be moved down near where the setuid() is.
  405. */
  406. if (setgid(pwd->pw_gid) != 0) {
  407. pam_end(pamh, PAM_SYSTEM_ERR);
  408. goto error;
  409. }
  410. if (initgroups(locuser, pwd->pw_gid) != 0) {
  411. pam_end(pamh, PAM_SYSTEM_ERR);
  412. goto error;
  413. }
  414. last_pam_msg = NULL;
  415. retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
  416. }
  417. if (retcode == PAM_SUCCESS) {
  418. last_pam_msg = NULL;
  419. retcode = pam_open_session(pamh, 0);
  420. }
  421. if (retcode != PAM_SUCCESS) {
  422. pam_end(pamh, retcode);
  423. if (last_pam_msg != NULL) {
  424. /* Dump all pam messages to syslog, Send only the
  425. * last message to the user
  426. */
  427. ListIterator itr = list_iterator_create(pam_msgs);
  428. char *msg;
  429. while (msg = (char *)list_next(itr))
  430. syslog(LOG_ERR, "pam_msg: %s\n", msg);
  431. list_iterator_destroy(itr);
  432. snprintf(pam_errmsgbuf, PAM_ERRMSGLEN, "%s", last_pam_msg);
  433. pam_errmsg = pam_errmsgbuf;
  434. return -1;
  435. }
  436. goto error;
  437. }
  438. return 0;
  439. error:
  440. if (pam_msgs)
  441. list_destroy(pam_msgs);
  442. syslog(LOG_ERR, "PAM Authentication Failure\n");
  443. pam_errmsg = "Permission Denied";
  444. return -1;
  445. }
  446. #endif /* USE_PAM */
  447. #define REMOTE_PREFIX "QSHELL_REMOTE_"
  448. /*
  449. * Return nonzero if environment variable 'var' is a qshell "wrapped"
  450. * environment variable, i.e. one that should be reset in the environment
  451. * without the REMOTE_PREFIX
  452. */
  453. static int _is_wrapped_qshell_env_var (const char *var)
  454. {
  455. const char * valid_vars[] = { "LD_LIBRARY_PATH", "LD_PREOPEN", NULL };
  456. const char *p, *q;
  457. if (strncmp (var, REMOTE_PREFIX, strlen (REMOTE_PREFIX)) != 0)
  458. return (0);
  459. p = var + strlen (REMOTE_PREFIX);
  460. for (q = valid_vars[0]; q != NULL; q++) {
  461. if (strncmp (p, q, strlen (q)) == 0)
  462. return (1);
  463. }
  464. return (0);
  465. }
  466. /*
  467. * Read Qshell arguments from remote side (via stdin)
  468. *
  469. * Returns completed Elan capability struct and qsw_info in
  470. * `cap' and `qinfo' on success. Returns -1 on failure.
  471. *
  472. */
  473. int get_qshell_info(ELAN_CAPABILITY *cap, qsw_info_t *qinfo,
  474. char *cwd, int cwdlen) {
  475. int i, envcount;
  476. char envstr[4096];
  477. char tmpstr[1024];
  478. /* read cwd of client - will change to cwd on remote side */
  479. if (getstr(cwd, cwdlen, "cwd") < 0)
  480. return -1;
  481. /* read environment of client - will replicate on remote side */
  482. if (getstr(tmpstr, sizeof(tmpstr), "envcount") < 0)
  483. return -1;
  484. envcount = atoi(tmpstr);
  485. while (envcount-- > 0) {
  486. if (getstr(envstr, sizeof(envstr), "envstr") < 0)
  487. return -1;
  488. /* Following is a mem-leak on some systems, on others it is
  489. * the proper way to call putenv.
  490. */
  491. /*
  492. * For variables with REMOTE_PREFIX, set the real environment
  493. * variable (e.g. LD_LIBRARY_PATH)
  494. */
  495. if (_is_wrapped_qshell_env_var (envstr))
  496. putenv(strdup(envstr + strlen(REMOTE_PREFIX)));
  497. else
  498. putenv(strdup(envstr));
  499. }
  500. /* read elan capability */
  501. if (getstr(tmpstr, sizeof(tmpstr), "capability") < 0)
  502. return -1;
  503. if (qsw_decode_cap(tmpstr, cap) < 0) {
  504. errlog("error reading capability: %s\n", tmpstr);
  505. return -1;
  506. }
  507. for (i = 0; i < qsw_cap_bitmap_count(); i += 16) {
  508. if (getstr(tmpstr, sizeof(tmpstr), "capability") < 0)
  509. return -1;
  510. if (qsw_decode_cap_bitmap(tmpstr, cap, i) < 0) {
  511. errlog("error reading capability bitmap(%d): %s\n", i, tmpstr);
  512. return -1;
  513. }
  514. }
  515. /* read info structure */
  516. if (getstr(tmpstr, sizeof(tmpstr), "qsw info") < 0)
  517. return -1;
  518. if (qsw_decode_info(tmpstr, qinfo) < 0) {
  519. errlog("error reading qsw info: %s\n", tmpstr);
  520. return -1;
  521. }
  522. return 0;
  523. }
  524. void stderr_parent(int sock, int pype, int prgid) {
  525. fd_set ready, readfrom;
  526. char buf[BUFSIZ], sig;
  527. int one = 1;
  528. int nfd, cc, guys = 2;
  529. ioctl(pype, FIONBIO, (char *) &one);
  530. /* should set s nbio! */
  531. FD_ZERO(&readfrom);
  532. FD_SET(sock, &readfrom);
  533. FD_SET(pype, &readfrom);
  534. nfd = (pype > sock) ? pype + 1 : sock + 1;
  535. while (guys > 0) {
  536. ready = readfrom;
  537. if (select(nfd, &ready, NULL, NULL, NULL) < 0) {
  538. if (errno != EINTR)
  539. break;
  540. continue;
  541. }
  542. if (FD_ISSET(sock, &ready)) {
  543. if (read(sock, &sig, 1) <= 0) {
  544. FD_CLR(sock, &readfrom);
  545. guys--;
  546. if (guys == 1)
  547. /* pdsh client has crashed, kill the job */
  548. qsw_prgsignal(prgid, SIGKILL);
  549. }
  550. else
  551. qsw_prgsignal(prgid, sig);
  552. }
  553. if (FD_ISSET(pype, &ready)) {
  554. if ((cc = read(pype, buf, sizeof(buf))) <= 0) {
  555. shutdown(sock, 2);
  556. FD_CLR(pype, &readfrom);
  557. guys--;
  558. } else {
  559. if (guys == 2)
  560. write(sock, buf, cc);
  561. }
  562. }
  563. }
  564. #ifdef USE_PAM
  565. /* Must close here, b/c other process runs exec() */
  566. pam_close_session(pamh, 0);
  567. pam_end(pamh, PAM_SUCCESS);
  568. #endif
  569. exit(0);
  570. }
  571. static int _qshell_init(void) {
  572. int port;
  573. signal(SIGINT, SIG_DFL);
  574. signal(SIGQUIT, SIG_DFL);
  575. signal(SIGTERM, SIG_DFL);
  576. /*
  577. * Remote side sends us a stringified port number to send back
  578. * stderr on. If zero, stderr is folded in with stdout.
  579. */
  580. alarm(60);
  581. if ((port = getint()) < 0) {
  582. syslog(LOG_ERR, "can't read port: %m");
  583. exit(1);
  584. }
  585. alarm(0);
  586. return port;
  587. }
  588. /*
  589. * Execute the Elan MPI job given the qshell args in `args'
  590. */
  591. static void _qshell_execute(struct qshell_args *args)
  592. {
  593. int ifd, pid, pv[2];
  594. ELAN_CAPABILITY cap;
  595. qsw_info_t qinfo;
  596. char *theshell, *shellname;
  597. char cwd[MAXPATHLEN+1];
  598. if (get_qshell_info(&cap, &qinfo, cwd, sizeof(cwd)) < 0)
  599. exit(1);
  600. /* A single \0 written back on the socket indicates success. Error
  601. * would be indicated by a nonzero code followed by error string,
  602. * e.g. through calling error().
  603. */
  604. if (write(2, "\0", 1) != 1) {
  605. errlog("%s: bad write of null to stdout: %s\n",
  606. typename, strerror(errno));
  607. exit(1);
  608. }
  609. sent_null = 1;
  610. /*
  611. * Fork off a process to send back stderr if requested.
  612. */
  613. if (args->port) {
  614. if (pipe(pv) < 0) {
  615. error("Can't make pipe.\n");
  616. exit(1);
  617. }
  618. if ((pid = fork()) == -1) {
  619. error("Can't fork; try again.\n");
  620. exit(1);
  621. }
  622. if (pid) {
  623. close(0);
  624. close(1);
  625. close(2);
  626. close(pv[1]);
  627. stderr_parent(args->sock, pv[0], qinfo.prgnum);
  628. /* NOTREACHED */
  629. }
  630. setpgrp();
  631. close(args->sock);
  632. close(pv[0]);
  633. dup2(pv[1], 2);
  634. close(pv[1]);
  635. }
  636. /*
  637. * Set up quadrics Elan capabilities and program desc.
  638. * Fork a couple of times in here.
  639. * On error, send diagnostics to stderr and exit.
  640. */
  641. qsw_setup_program(&cap, &qinfo, args->pwd->pw_uid);
  642. /*
  643. * Become the locuser, etc. etc. then exec the shell command.
  644. */
  645. /* set the path to the shell */
  646. theshell = args->pwd->pw_shell;
  647. if (!theshell || !*theshell)
  648. theshell = _PATH_BSHELL;
  649. #if BSD > 43
  650. if (setlogin(args->pwd->pw_name) < 0)
  651. errlog("setlogin() failed: %s\n", strerror(errno));
  652. #endif
  653. #ifndef USE_PAM
  654. /* if PAM, already done */
  655. if (setgid(args->pwd->pw_gid)) {
  656. errlog("setgid: %s\n", strerror(errno));
  657. exit(1);
  658. }
  659. if (initgroups(args->pwd->pw_name, args->pwd->pw_gid)) {
  660. errlog("initgroups: %s\n", strerror(errno));
  661. exit(1);
  662. }
  663. #endif
  664. if (setuid(args->pwd->pw_uid)) {
  665. errlog("setuid: %s\n", strerror(errno));
  666. exit(1);
  667. }
  668. strncat(homedir, args->pwd->pw_dir, sizeof(homedir) - 6);
  669. homedir[sizeof(homedir) - 1] = 0;
  670. strcat(path, _PATH_DEFPATH);
  671. strncat(shell, theshell, sizeof(shell) - 7);
  672. shell[sizeof(shell) - 1] = 0;
  673. strncat(username, args->pwd->pw_name, sizeof(username) - 6);
  674. username[sizeof(username) - 1] = 0;
  675. if ((shellname = strrchr(theshell, '/')) == NULL)
  676. shellname = theshell;
  677. else
  678. shellname++;
  679. if (paranoid) {
  680. syslog(LOG_INFO | LOG_AUTH, "%s@%s as %s: cmd='%s' cwd='%s'",
  681. (args->remuser == NULL) ? args->pwd->pw_name : args->remuser,
  682. args->hostname,
  683. (args->locuser == NULL) ? args->pwd->pw_name : args->locuser,
  684. args->cmdbuf, cwd);
  685. }
  686. endpwent();
  687. /* override USER, HOME, SHELL environment vars */
  688. putenv(username);
  689. putenv(homedir);
  690. putenv(shell);
  691. /* change to client working dir */
  692. if (chdir(cwd) < 0) {
  693. errlog("chdir to client working directory: %s\n", strerror(errno));
  694. exit(1);
  695. }
  696. /*
  697. * Close all fds, in case libc has left fun stuff like
  698. * /etc/shadow open.
  699. */
  700. #define FDS_INUSE 2
  701. for (ifd = getdtablesize() - 1; ifd > FDS_INUSE; ifd--)
  702. close(ifd);
  703. execl(theshell, shellname, "-c", args->cmdbuf, 0);
  704. errlog("failed to exec shell: %s\n", strerror(errno));
  705. exit(1);
  706. }
  707. void network_init(int fd, struct sockaddr_in *fromp, int check_port) {
  708. struct linger linger;
  709. socklen_t fromlen;
  710. int on = 1;
  711. int port;
  712. fromlen = sizeof(*fromp);
  713. if (getpeername(fd, (struct sockaddr *) fromp, &fromlen) < 0) {
  714. syslog(LOG_ERR, "getpeername: %m");
  715. _exit(1);
  716. }
  717. if (keepalive &&
  718. setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
  719. syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
  720. linger.l_onoff = 1;
  721. linger.l_linger = 60; /* XXX */
  722. if (setsockopt(fd, SOL_SOCKET, SO_LINGER,
  723. (char *) &linger, sizeof(linger)) < 0)
  724. syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
  725. if (fromp->sin_family != AF_INET) {
  726. syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", fromp->sin_family);
  727. exit(1);
  728. }
  729. #ifdef IP_OPTIONS
  730. {
  731. u_char optbuf[BUFSIZ / 3], *cp;
  732. char lbuf[BUFSIZ + 1], *lp;
  733. socklen_t optsize = sizeof(optbuf);
  734. int ipproto;
  735. struct protoent *ip;
  736. if ((ip = getprotobyname("ip")) != NULL)
  737. ipproto = ip->p_proto;
  738. else
  739. ipproto = IPPROTO_IP;
  740. if (!getsockopt(0, ipproto, IP_OPTIONS, (char *) optbuf, &optsize) &&
  741. optsize != 0) {
  742. lp = lbuf;
  743. /*
  744. * If these are true, this will not run off the end of lbuf[].
  745. */
  746. assert(optsize <= BUFSIZ / 3);
  747. assert(3 * optsize <= BUFSIZ);
  748. for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3)
  749. snprintf(lp, 4, " %2.2x", *cp);
  750. syslog(LOG_NOTICE, "Connection received from %s using IP options"
  751. " (ignored): %s", inet_ntoa(fromp->sin_addr), lbuf);
  752. if (setsockopt(0, ipproto, IP_OPTIONS, NULL, optsize) != 0) {
  753. syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m");
  754. exit(1);
  755. }
  756. }
  757. }
  758. #endif
  759. /*
  760. * Check originating port for validity.
  761. */
  762. if (check_port) {
  763. port = ntohs(fromp->sin_port);
  764. if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED / 2) {
  765. syslog(LOG_NOTICE | LOG_AUTH, "Connection from %s on illegal port",
  766. inet_ntoa(fromp->sin_addr));
  767. exit(1);
  768. }
  769. }
  770. }
  771. int qshell(int argc, char *argv[], QshGetArgsF getargs, char *name,
  772. int check_port) {
  773. int ch;
  774. struct sockaddr_in from;
  775. struct qshell_args args;
  776. _check_rhosts_file=1;
  777. strncpy(typename, name, BUFSIZ);
  778. openlog(typename, LOG_PID | LOG_ODELAY, LOG_DAEMON);
  779. err_init(xbasename(argv[0]));
  780. opterr = 0;
  781. while ((ch = getopt(argc, argv, OPTIONS)) != EOF) {
  782. switch (ch) {
  783. case 'a':
  784. check_all = 1;
  785. break;
  786. case 'h':
  787. allow_root_rhosts = 1;
  788. break;
  789. case 'l':
  790. _check_rhosts_file = 0;
  791. break;
  792. case 'n':
  793. keepalive = 0;
  794. break;
  795. case 'L':
  796. paranoid = 1;
  797. break;
  798. case '?':
  799. default:
  800. syslog(LOG_ERR, "usage: %s [-%s]", typename, OPTIONS);
  801. exit(2);
  802. }
  803. }
  804. argc -= optind;
  805. argv += optind;
  806. network_init(0, &from, check_port);
  807. if (qsw_init() < 0) {
  808. syslog(LOG_ERR, "qsw_init failed. Exiting...");
  809. exit(1);
  810. }
  811. qsw_spawn_neterr_thr();
  812. args.port = _qshell_init();
  813. getargs(&from, &args);
  814. _qshell_execute(&args);
  815. qsw_fini();
  816. return 0;
  817. }
  818. /*
  819. * vi: tabstop=4 shiftwidth=4 expandtab
  820. */