/src/modules/k4cmd.c

https://code.google.com/ · C · 384 lines · 253 code · 35 blank · 96 comment · 54 complexity · 969207fb554b78ab5a392ef928859098 MD5 · raw file

  1. /*****************************************************************************\
  2. * $Id$
  3. *****************************************************************************
  4. * Copyright (C) 2001-2002 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. * This code is based on the MIT Kerberos IV kcmd.c with some athena hacks
  28. * removed and MT safety added, and the interface changed. Original UC regents
  29. * header from BSD included below.
  30. *
  31. * XXX for some reason it takes a really long time (several seconds per node)
  32. * to acquire service tickets! Why?
  33. */
  34. /*
  35. * Copyright (c) 1983 Regents of the University of California. All rights
  36. * reserved.
  37. *
  38. * Redistribution and use in source and binary forms are permitted provided that
  39. * the above copyright notice and this paragraph are duplicated in all such
  40. * forms and that any documentation, advertising materials, and other
  41. * materials related to such distribution and use acknowledge that the
  42. * software was developed by the University of California, Berkeley. The
  43. * name of the University may not be used to endorse or promote products
  44. * derived from this software without specific prior written permission. THIS
  45. * SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  46. * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  47. * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  48. */
  49. #if HAVE_CONFIG_H
  50. #include "config.h"
  51. #endif
  52. #if HAVE_PTHREAD_H
  53. #include <pthread.h>
  54. #endif
  55. #include <stdio.h>
  56. #include <string.h>
  57. #include <sys/types.h>
  58. #include <ctype.h>
  59. #include <pwd.h>
  60. #include <sys/param.h>
  61. #if HAVE_SYS_FILE_H
  62. #include <sys/file.h>
  63. #endif
  64. #include <signal.h>
  65. #include <sys/socket.h>
  66. #include <sys/stat.h>
  67. #include <netinet/in.h>
  68. #include <netdb.h>
  69. #include <errno.h>
  70. #include <krb.h>
  71. #include <kparse.h>
  72. #include <fcntl.h> /* for F_SETOWN */
  73. #include "src/common/xmalloc.h"
  74. #include "src/common/xstring.h"
  75. #include "src/common/macros.h"
  76. #include "src/common/err.h"
  77. #include "src/common/list.h"
  78. #include "src/common/xpoll.h"
  79. #include "src/pdsh/privsep.h"
  80. #ifndef MAXHOSTNAMELEN
  81. #define MAXHOSTNAMELEN 64
  82. #endif
  83. #define KCMD_PORT 544
  84. #if STATIC_MODULES
  85. # define pdsh_module_info k4cmd_module_info
  86. # define pdsh_module_priority k4cmd_module_priority
  87. #endif
  88. int pdsh_module_priority = DEFAULT_MODULE_PRIORITY;
  89. extern errno;
  90. extern char *inet_ntoa();
  91. static int k4cmd_init(opt_t *);
  92. static int k4cmd_signal(int, void *, int);
  93. static int k4cmd(char *, char *, char *, char *, char *, int, int *, void **);
  94. /*
  95. * Export pdsh module operations structure
  96. */
  97. struct pdsh_module_operations k4cmd_module_ops = {
  98. (ModInitF) NULL,
  99. (ModExitF) NULL,
  100. (ModReadWcollF) NULL,
  101. (ModPostOpF) NULL,
  102. };
  103. /*
  104. * Export rcmd module operations
  105. */
  106. struct pdsh_rcmd_operations k4cmd_rcmd_ops = {
  107. (RcmdInitF) k4cmd_init,
  108. (RcmdSigF) k4cmd_signal,
  109. (RcmdF) k4cmd,
  110. };
  111. /*
  112. * Export module options
  113. */
  114. struct pdsh_module_option k4cmd_module_options[] =
  115. {
  116. PDSH_OPT_TABLE_END
  117. };
  118. /*
  119. * k4cmd module info
  120. */
  121. struct pdsh_module pdsh_module_info = {
  122. "rcmd",
  123. "k4",
  124. "Jim Garlick <garlick@llnl.gov>",
  125. "kerberos based rcmd connect method",
  126. DSH | PCP,
  127. &k4cmd_module_ops,
  128. &k4cmd_rcmd_ops,
  129. &k4cmd_module_options[0],
  130. };
  131. static int k4cmd_init(opt_t * opt)
  132. {
  133. /* not implemented */
  134. return 0;
  135. }
  136. /*
  137. * Use rcmd backchannel to propagate signals.
  138. * efd (IN) file descriptor connected socket (-1 if not used)
  139. * signum (IN) signal number to send
  140. */
  141. static int k4cmd_signal(int efd, void *arg, int signum)
  142. {
  143. char c;
  144. if (efd >= 0) {
  145. /* set non-blocking mode for write - just take our best shot */
  146. if (fcntl(efd, F_SETFL, O_NONBLOCK) < 0)
  147. err("%p: fcntl: %m\n");
  148. c = (char) signum;
  149. write(efd, &c, 1);
  150. }
  151. return 0;
  152. }
  153. /*
  154. * The rcmd call itself.
  155. * ahost (IN) remote hostname
  156. * addr (IN) IP address
  157. * locuser (IN) local username
  158. * remuser (IN) remote username
  159. * cmd (IN) command to execute
  160. * rank (IN) MPI rank
  161. * fd2p (IN/OUT) if non-NULL, open stderr backchannel on this fd
  162. * s (RETURN) socket for stdout/sdin or -1 on failure
  163. */
  164. static int
  165. k4cmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd,
  166. int rank, int *fd2p, void **arg)
  167. {
  168. KTEXT_ST ticket; /* kerberos IV context */
  169. CREDENTIALS cred;
  170. Key_schedule schedule;
  171. MSG_DAT msg_data;
  172. struct sockaddr_in faddr;
  173. struct sockaddr_in laddr;
  174. int s, pid;
  175. sigset_t oldset, blockme;
  176. struct sockaddr_in sin, from;
  177. char c;
  178. int lport = IPPORT_RESERVED - 1;
  179. unsigned long krb_options = 0L;
  180. static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
  181. int status;
  182. int rc, rv;
  183. struct xpollfd xpfds[2];
  184. pid = getpid();
  185. sigemptyset(&blockme);
  186. sigaddset(&blockme, SIGURG);
  187. pthread_sigmask(SIG_BLOCK, &blockme, &oldset);
  188. for (;;) {
  189. s = privsep_rresvport(&lport);
  190. if (s < 0) {
  191. if (errno == EAGAIN)
  192. err("%p: %S: socket: All ports in use\n", ahost);
  193. else
  194. err("%p: %S: k4cmd: socket: %m\n", ahost);
  195. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  196. return (-1);
  197. }
  198. fcntl(s, F_SETOWN, pid);
  199. sin.sin_family = AF_INET;
  200. memcpy((caddr_t) & sin.sin_addr, addr, IP_ADDR_LEN);
  201. sin.sin_port = htons(KCMD_PORT);
  202. rv = connect(s, (struct sockaddr *) &sin, sizeof(sin));
  203. if (rv >= 0)
  204. break;
  205. (void) close(s);
  206. if (errno == EADDRINUSE) {
  207. lport--;
  208. continue;
  209. }
  210. if (errno == EINTR)
  211. err("%p: %S: connect timed out\n", ahost);
  212. else
  213. err("%p: %S: %m\n", ahost);
  214. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  215. return (-1);
  216. }
  217. if (fd2p == 0) {
  218. write(s, "", 1);
  219. lport = 0;
  220. } else {
  221. char num[8];
  222. int s2, s3;
  223. socklen_t len = sizeof(from);
  224. s2 = privsep_rresvport(&lport);
  225. if (s2 < 0) {
  226. goto bad;
  227. }
  228. listen(s2, 1);
  229. (void) snprintf(num, sizeof(num), "%d", lport);
  230. if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
  231. err("%p: %S: write: setting up stderr: %m\n", ahost);
  232. (void) close(s2);
  233. goto bad;
  234. }
  235. errno = 0;
  236. xpfds[0].fd = s;
  237. xpfds[1].fd = s2;
  238. xpfds[0].events = xpfds[1].events = XPOLLREAD;
  239. if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) {
  240. if (errno != 0)
  241. err("%p: %S: k4cmd: xpoll (setting up stderr): %m\n", ahost);
  242. else
  243. err("%p: %S: k4cmd: xpoll: protocol failure in circuit setup\n", ahost);
  244. (void) close(s2);
  245. goto bad;
  246. }
  247. s3 = accept(s2, (struct sockaddr *) &from, &len);
  248. (void) close(s2);
  249. if (s3 < 0) {
  250. err("%p: %S: accept: %m\n", ahost);
  251. lport = 0;
  252. goto bad;
  253. }
  254. *fd2p = s3;
  255. from.sin_port = ntohs((u_short) from.sin_port);
  256. }
  257. /*
  258. * Kerberos-authenticated service. Don't have to send locuser, since
  259. * its already in the ticket, and we'll extract it on the other side.
  260. */
  261. /*krb_options |= KOPT_DONT_CANON; */
  262. pthread_mutex_lock(&mylock);
  263. status = krb_sendauth(krb_options, s, &ticket, "rcmd", ahost,
  264. NULL, (unsigned long) pid, &msg_data,
  265. &cred, schedule, &laddr, &faddr, "KCMDV0.1");
  266. if (status != KSUCCESS) {
  267. /*
  268. * this part involves some very intimate knowledge of a
  269. * particular sendauth implementation to pry out the old
  270. * bits. This only catches the case of total failure -- but
  271. * that's the one where we get useful data from the remote
  272. * end. If we even get an authenticator back, then the
  273. * problem gets diagnosed locally anyhow.
  274. */
  275. extern KRB_INT32 __krb_sendauth_hidden_tkt_len;
  276. char *old_data = (char *) &__krb_sendauth_hidden_tkt_len;
  277. char tmpbuf[LINEBUFSIZE];
  278. char *p = tmpbuf;
  279. if ((status == KFAILURE) && (*old_data == 1)) {
  280. strncpy(tmpbuf, old_data + 1, 3);
  281. tmpbuf[3] = '\0';
  282. err("%p: %S: %s", ahost, tmpbuf);
  283. *old_data = (-1);
  284. }
  285. if ((status == KFAILURE) && (*old_data == (char) -1)) {
  286. while (read(s, &c, 1) == 1) {
  287. /*(void) write(2, &c, 1); */
  288. *p++ = c;
  289. if (c == '\n')
  290. break;
  291. }
  292. *p++ = '\0';
  293. err("%p: %S: %s", ahost, tmpbuf);
  294. status = -1;
  295. }
  296. switch (status) {
  297. case KDC_PR_UNKNOWN:
  298. err("%p: %S: not registered for kerberos\n", ahost);
  299. break;
  300. case NO_TKT_FIL:
  301. err("%p: %S: no tickets file found\n", ahost);
  302. break;
  303. default:
  304. err("%p: %S: k4cmd failed: %s\n", ahost,
  305. (status == -1) ? "k4cmd protocol failure" :
  306. krb_get_err_text(status));
  307. }
  308. pthread_mutex_unlock(&mylock);
  309. goto bad2;
  310. }
  311. pthread_mutex_unlock(&mylock);
  312. (void) write(s, remuser, strlen(remuser) + 1);
  313. (void) write(s, cmd, strlen(cmd) + 1);
  314. if ((rc = read(s, &c, 1)) != 1) {
  315. if (rc == -1) {
  316. err("%p: %S: read: %m\n", ahost);
  317. } else {
  318. err("%p: %S: k4cmd: bad connection with remote host\n", ahost);
  319. }
  320. goto bad2;
  321. }
  322. if (c != 0) {
  323. /* retrieve error string from remote server */
  324. char tmpbuf[LINEBUFSIZE];
  325. char *p = tmpbuf;
  326. while (read(s, &c, 1) == 1) {
  327. *p++ = c;
  328. if (c == '\n')
  329. break;
  330. }
  331. if (c != '\n')
  332. *p++ = '\n';
  333. *p++ = '\0';
  334. err("%S: %s", ahost, tmpbuf);
  335. goto bad2;
  336. }
  337. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  338. return (s);
  339. bad2:
  340. if (lport)
  341. (void) close(*fd2p);
  342. bad:
  343. (void) close(s);
  344. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  345. return (-1);
  346. }
  347. /*
  348. * vi:tabstop=4 shiftwidth=4 expandtab
  349. */