svr-chansession.c 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034
  1. /*
  2. * Dropbear - a SSH2 server
  3. *
  4. * Copyright (c) 2002,2003 Matt Johnston
  5. * All rights reserved.
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. * SOFTWARE. */
  24. #include "includes.h"
  25. #include "packet.h"
  26. #include "buffer.h"
  27. #include "session.h"
  28. #include "dbutil.h"
  29. #include "channel.h"
  30. #include "chansession.h"
  31. #include "sshpty.h"
  32. #include "termcodes.h"
  33. #include "ssh.h"
  34. #include "dbrandom.h"
  35. #include "x11fwd.h"
  36. #include "agentfwd.h"
  37. #include "runopts.h"
  38. #include "auth.h"
  39. /* Handles sessions (either shells or programs) requested by the client */
  40. static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
  41. int iscmd, int issubsys);
  42. static int sessionpty(struct ChanSess * chansess);
  43. static int sessionsignal(struct ChanSess *chansess);
  44. static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
  45. static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
  46. static int sessionwinchange(struct ChanSess *chansess);
  47. static void execchild(void *user_data_chansess);
  48. static void addchildpid(struct ChanSess *chansess, pid_t pid);
  49. static void sesssigchild_handler(int val);
  50. static void closechansess(struct Channel *channel);
  51. static int newchansess(struct Channel *channel);
  52. static void chansessionrequest(struct Channel *channel);
  53. static int sesscheckclose(struct Channel *channel);
  54. static void send_exitsignalstatus(struct Channel *channel);
  55. static void send_msg_chansess_exitstatus(struct Channel * channel,
  56. struct ChanSess * chansess);
  57. static void send_msg_chansess_exitsignal(struct Channel * channel,
  58. struct ChanSess * chansess);
  59. static void get_termmodes(struct ChanSess *chansess);
  60. const struct ChanType svrchansess = {
  61. 0, /* sepfds */
  62. "session", /* name */
  63. newchansess, /* inithandler */
  64. sesscheckclose, /* checkclosehandler */
  65. chansessionrequest, /* reqhandler */
  66. closechansess, /* closehandler */
  67. };
  68. /* required to clear environment */
  69. extern char** environ;
  70. static int sesscheckclose(struct Channel *channel) {
  71. struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
  72. TRACE(("sesscheckclose, pid is %d", chansess->exit.exitpid))
  73. return chansess->exit.exitpid != -1;
  74. }
  75. /* Handler for childs exiting, store the state for return to the client */
  76. /* There's a particular race we have to watch out for: if the forked child
  77. * executes, exits, and this signal-handler is called, all before the parent
  78. * gets to run, then the childpids[] array won't have the pid in it. Hence we
  79. * use the svr_ses.lastexit struct to hold the exit, which is then compared by
  80. * the parent when it runs. This work correctly at least in the case of a
  81. * single shell spawned (ie the usual case) */
  82. static void sesssigchild_handler(int UNUSED(dummy)) {
  83. int status;
  84. pid_t pid;
  85. unsigned int i;
  86. struct sigaction sa_chld;
  87. struct exitinfo *exit = NULL;
  88. const int saved_errno = errno;
  89. /* Make channel handling code look for closed channels */
  90. ses.channel_signal_pending = 1;
  91. TRACE(("enter sigchld handler"))
  92. while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
  93. TRACE(("sigchld handler: pid %d", pid))
  94. exit = NULL;
  95. /* find the corresponding chansess */
  96. for (i = 0; i < svr_ses.childpidsize; i++) {
  97. if (svr_ses.childpids[i].pid == pid) {
  98. TRACE(("found match session"));
  99. exit = &svr_ses.childpids[i].chansess->exit;
  100. break;
  101. }
  102. }
  103. /* If the pid wasn't matched, then we might have hit the race mentioned
  104. * above. So we just store the info for the parent to deal with */
  105. if (exit == NULL) {
  106. TRACE(("using lastexit"));
  107. exit = &svr_ses.lastexit;
  108. }
  109. exit->exitpid = pid;
  110. if (WIFEXITED(status)) {
  111. exit->exitstatus = WEXITSTATUS(status);
  112. }
  113. if (WIFSIGNALED(status)) {
  114. exit->exitsignal = WTERMSIG(status);
  115. #if !defined(AIX) && defined(WCOREDUMP)
  116. exit->exitcore = WCOREDUMP(status);
  117. #else
  118. exit->exitcore = 0;
  119. #endif
  120. } else {
  121. /* we use this to determine how pid exited */
  122. exit->exitsignal = -1;
  123. }
  124. /* Make sure that the main select() loop wakes up */
  125. while (1) {
  126. /* isserver is just a random byte to write. We can't do anything
  127. about an error so should just ignore it */
  128. if (write(ses.signal_pipe[1], &ses.isserver, 1) == 1
  129. || errno != EINTR) {
  130. break;
  131. }
  132. }
  133. }
  134. sa_chld.sa_handler = sesssigchild_handler;
  135. sa_chld.sa_flags = SA_NOCLDSTOP;
  136. sigemptyset(&sa_chld.sa_mask);
  137. sigaction(SIGCHLD, &sa_chld, NULL);
  138. TRACE(("leave sigchld handler"))
  139. errno = saved_errno;
  140. }
  141. /* send the exit status or the signal causing termination for a session */
  142. static void send_exitsignalstatus(struct Channel *channel) {
  143. struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
  144. if (chansess->exit.exitpid >= 0) {
  145. if (chansess->exit.exitsignal > 0) {
  146. send_msg_chansess_exitsignal(channel, chansess);
  147. } else {
  148. send_msg_chansess_exitstatus(channel, chansess);
  149. }
  150. }
  151. }
  152. /* send the exitstatus to the client */
  153. static void send_msg_chansess_exitstatus(struct Channel * channel,
  154. struct ChanSess * chansess) {
  155. dropbear_assert(chansess->exit.exitpid != -1);
  156. dropbear_assert(chansess->exit.exitsignal == -1);
  157. CHECKCLEARTOWRITE();
  158. buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
  159. buf_putint(ses.writepayload, channel->remotechan);
  160. buf_putstring(ses.writepayload, "exit-status", 11);
  161. buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
  162. buf_putint(ses.writepayload, chansess->exit.exitstatus);
  163. encrypt_packet();
  164. }
  165. /* send the signal causing the exit to the client */
  166. static void send_msg_chansess_exitsignal(struct Channel * channel,
  167. struct ChanSess * chansess) {
  168. int i;
  169. char* signame = NULL;
  170. dropbear_assert(chansess->exit.exitpid != -1);
  171. dropbear_assert(chansess->exit.exitsignal > 0);
  172. TRACE(("send_msg_chansess_exitsignal %d", chansess->exit.exitsignal))
  173. CHECKCLEARTOWRITE();
  174. /* we check that we can match a signal name, otherwise
  175. * don't send anything */
  176. for (i = 0; signames[i].name != NULL; i++) {
  177. if (signames[i].signal == chansess->exit.exitsignal) {
  178. signame = signames[i].name;
  179. break;
  180. }
  181. }
  182. if (signame == NULL) {
  183. return;
  184. }
  185. buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
  186. buf_putint(ses.writepayload, channel->remotechan);
  187. buf_putstring(ses.writepayload, "exit-signal", 11);
  188. buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
  189. buf_putstring(ses.writepayload, signame, strlen(signame));
  190. buf_putbyte(ses.writepayload, chansess->exit.exitcore);
  191. buf_putstring(ses.writepayload, "", 0); /* error msg */
  192. buf_putstring(ses.writepayload, "", 0); /* lang */
  193. encrypt_packet();
  194. }
  195. /* set up a session channel */
  196. static int newchansess(struct Channel *channel) {
  197. struct ChanSess *chansess;
  198. TRACE(("new chansess %p", (void*)channel))
  199. dropbear_assert(channel->typedata == NULL);
  200. chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
  201. chansess->cmd = NULL;
  202. chansess->connection_string = NULL;
  203. chansess->client_string = NULL;
  204. chansess->pid = 0;
  205. /* pty details */
  206. chansess->master = -1;
  207. chansess->slave = -1;
  208. chansess->tty = NULL;
  209. chansess->term = NULL;
  210. chansess->exit.exitpid = -1;
  211. channel->typedata = chansess;
  212. #ifndef DISABLE_X11FWD
  213. chansess->x11listener = NULL;
  214. chansess->x11authprot = NULL;
  215. chansess->x11authcookie = NULL;
  216. #endif
  217. #ifdef ENABLE_SVR_AGENTFWD
  218. chansess->agentlistener = NULL;
  219. chansess->agentfile = NULL;
  220. chansess->agentdir = NULL;
  221. #endif
  222. channel->prio = DROPBEAR_CHANNEL_PRIO_INTERACTIVE;
  223. return 0;
  224. }
  225. static struct logininfo*
  226. chansess_login_alloc(struct ChanSess *chansess) {
  227. struct logininfo * li;
  228. li = login_alloc_entry(chansess->pid, ses.authstate.username,
  229. svr_ses.remotehost, chansess->tty);
  230. return li;
  231. }
  232. /* clean a session channel */
  233. static void closechansess(struct Channel *channel) {
  234. struct ChanSess *chansess;
  235. unsigned int i;
  236. struct logininfo *li;
  237. TRACE(("enter closechansess"))
  238. chansess = (struct ChanSess*)channel->typedata;
  239. if (chansess == NULL) {
  240. TRACE(("leave closechansess: chansess == NULL"))
  241. return;
  242. }
  243. send_exitsignalstatus(channel);
  244. m_free(chansess->cmd);
  245. m_free(chansess->term);
  246. #ifdef ENABLE_SVR_PUBKEY_OPTIONS
  247. m_free(chansess->original_command);
  248. #endif
  249. if (chansess->tty) {
  250. /* write the utmp/wtmp login record */
  251. li = chansess_login_alloc(chansess);
  252. login_logout(li);
  253. login_free_entry(li);
  254. pty_release(chansess->tty);
  255. m_free(chansess->tty);
  256. }
  257. #ifndef DISABLE_X11FWD
  258. x11cleanup(chansess);
  259. #endif
  260. #ifdef ENABLE_SVR_AGENTFWD
  261. svr_agentcleanup(chansess);
  262. #endif
  263. /* clear child pid entries */
  264. for (i = 0; i < svr_ses.childpidsize; i++) {
  265. if (svr_ses.childpids[i].chansess == chansess) {
  266. dropbear_assert(svr_ses.childpids[i].pid > 0);
  267. TRACE(("closing pid %d", svr_ses.childpids[i].pid))
  268. TRACE(("exitpid is %d", chansess->exit.exitpid))
  269. svr_ses.childpids[i].pid = -1;
  270. svr_ses.childpids[i].chansess = NULL;
  271. }
  272. }
  273. m_free(chansess);
  274. TRACE(("leave closechansess"))
  275. }
  276. /* Handle requests for a channel. These can be execution requests,
  277. * or x11/authagent forwarding. These are passed to appropriate handlers */
  278. static void chansessionrequest(struct Channel *channel) {
  279. char * type = NULL;
  280. unsigned int typelen;
  281. unsigned char wantreply;
  282. int ret = 1;
  283. struct ChanSess *chansess;
  284. TRACE(("enter chansessionrequest"))
  285. type = buf_getstring(ses.payload, &typelen);
  286. wantreply = buf_getbool(ses.payload);
  287. if (typelen > MAX_NAME_LEN) {
  288. TRACE(("leave chansessionrequest: type too long")) /* XXX send error?*/
  289. goto out;
  290. }
  291. chansess = (struct ChanSess*)channel->typedata;
  292. dropbear_assert(chansess != NULL);
  293. TRACE(("type is %s", type))
  294. if (strcmp(type, "window-change") == 0) {
  295. ret = sessionwinchange(chansess);
  296. } else if (strcmp(type, "shell") == 0) {
  297. ret = sessioncommand(channel, chansess, 0, 0);
  298. } else if (strcmp(type, "pty-req") == 0) {
  299. ret = sessionpty(chansess);
  300. } else if (strcmp(type, "exec") == 0) {
  301. ret = sessioncommand(channel, chansess, 1, 0);
  302. } else if (strcmp(type, "subsystem") == 0) {
  303. ret = sessioncommand(channel, chansess, 1, 1);
  304. #ifndef DISABLE_X11FWD
  305. } else if (strcmp(type, "x11-req") == 0) {
  306. ret = x11req(chansess);
  307. #endif
  308. #ifdef ENABLE_SVR_AGENTFWD
  309. } else if (strcmp(type, "auth-agent-req@openssh.com") == 0) {
  310. ret = svr_agentreq(chansess);
  311. #endif
  312. } else if (strcmp(type, "signal") == 0) {
  313. ret = sessionsignal(chansess);
  314. } else {
  315. /* etc, todo "env", "subsystem" */
  316. }
  317. out:
  318. if (wantreply) {
  319. if (ret == DROPBEAR_SUCCESS) {
  320. send_msg_channel_success(channel);
  321. } else {
  322. send_msg_channel_failure(channel);
  323. }
  324. }
  325. m_free(type);
  326. TRACE(("leave chansessionrequest"))
  327. }
  328. /* Send a signal to a session's process as requested by the client*/
  329. static int sessionsignal(struct ChanSess *chansess) {
  330. int sig = 0;
  331. char* signame = NULL;
  332. int i;
  333. if (chansess->pid == 0) {
  334. /* haven't got a process pid yet */
  335. return DROPBEAR_FAILURE;
  336. }
  337. signame = buf_getstring(ses.payload, NULL);
  338. i = 0;
  339. while (signames[i].name != 0) {
  340. if (strcmp(signames[i].name, signame) == 0) {
  341. sig = signames[i].signal;
  342. break;
  343. }
  344. i++;
  345. }
  346. m_free(signame);
  347. if (sig == 0) {
  348. /* failed */
  349. return DROPBEAR_FAILURE;
  350. }
  351. if (kill(chansess->pid, sig) < 0) {
  352. return DROPBEAR_FAILURE;
  353. }
  354. return DROPBEAR_SUCCESS;
  355. }
  356. /* Let the process know that the window size has changed, as notified from the
  357. * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
  358. static int sessionwinchange(struct ChanSess *chansess) {
  359. int termc, termr, termw, termh;
  360. if (chansess->master < 0) {
  361. /* haven't got a pty yet */
  362. return DROPBEAR_FAILURE;
  363. }
  364. termc = buf_getint(ses.payload);
  365. termr = buf_getint(ses.payload);
  366. termw = buf_getint(ses.payload);
  367. termh = buf_getint(ses.payload);
  368. pty_change_window_size(chansess->master, termr, termc, termw, termh);
  369. return DROPBEAR_SUCCESS;
  370. }
  371. static void get_termmodes(struct ChanSess *chansess) {
  372. struct termios termio;
  373. unsigned char opcode;
  374. unsigned int value;
  375. const struct TermCode * termcode;
  376. unsigned int len;
  377. TRACE(("enter get_termmodes"))
  378. /* Term modes */
  379. /* We'll ignore errors and continue if we can't set modes.
  380. * We're ignoring baud rates since they seem evil */
  381. if (tcgetattr(chansess->master, &termio) == -1) {
  382. return;
  383. }
  384. len = buf_getint(ses.payload);
  385. TRACE(("term mode str %d p->l %d p->p %d",
  386. len, ses.payload->len , ses.payload->pos));
  387. if (len != ses.payload->len - ses.payload->pos) {
  388. dropbear_exit("Bad term mode string");
  389. }
  390. if (len == 0) {
  391. TRACE(("leave get_termmodes: empty terminal modes string"))
  392. return;
  393. }
  394. while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) {
  395. /* must be before checking type, so that value is consumed even if
  396. * we don't use it */
  397. value = buf_getint(ses.payload);
  398. /* handle types of code */
  399. if (opcode > MAX_TERMCODE) {
  400. continue;
  401. }
  402. termcode = &termcodes[(unsigned int)opcode];
  403. switch (termcode->type) {
  404. case TERMCODE_NONE:
  405. break;
  406. case TERMCODE_CONTROLCHAR:
  407. termio.c_cc[termcode->mapcode] = value;
  408. break;
  409. case TERMCODE_INPUT:
  410. if (value) {
  411. termio.c_iflag |= termcode->mapcode;
  412. } else {
  413. termio.c_iflag &= ~(termcode->mapcode);
  414. }
  415. break;
  416. case TERMCODE_OUTPUT:
  417. if (value) {
  418. termio.c_oflag |= termcode->mapcode;
  419. } else {
  420. termio.c_oflag &= ~(termcode->mapcode);
  421. }
  422. break;
  423. case TERMCODE_LOCAL:
  424. if (value) {
  425. termio.c_lflag |= termcode->mapcode;
  426. } else {
  427. termio.c_lflag &= ~(termcode->mapcode);
  428. }
  429. break;
  430. case TERMCODE_CONTROL:
  431. if (value) {
  432. termio.c_cflag |= termcode->mapcode;
  433. } else {
  434. termio.c_cflag &= ~(termcode->mapcode);
  435. }
  436. break;
  437. }
  438. }
  439. if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
  440. dropbear_log(LOG_INFO, "Error setting terminal attributes");
  441. }
  442. TRACE(("leave get_termmodes"))
  443. }
  444. /* Set up a session pty which will be used to execute the shell or program.
  445. * The pty is allocated now, and kept for when the shell/program executes.
  446. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
  447. static int sessionpty(struct ChanSess * chansess) {
  448. unsigned int termlen;
  449. char namebuf[65];
  450. struct passwd * pw = NULL;
  451. TRACE(("enter sessionpty"))
  452. if (!svr_pubkey_allows_pty()) {
  453. TRACE(("leave sessionpty : pty forbidden by public key option"))
  454. return DROPBEAR_FAILURE;
  455. }
  456. chansess->term = buf_getstring(ses.payload, &termlen);
  457. if (termlen > MAX_TERM_LEN) {
  458. /* TODO send disconnect ? */
  459. TRACE(("leave sessionpty: term len too long"))
  460. return DROPBEAR_FAILURE;
  461. }
  462. /* allocate the pty */
  463. if (chansess->master != -1) {
  464. dropbear_exit("Multiple pty requests");
  465. }
  466. if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) {
  467. TRACE(("leave sessionpty: failed to allocate pty"))
  468. return DROPBEAR_FAILURE;
  469. }
  470. chansess->tty = m_strdup(namebuf);
  471. if (!chansess->tty) {
  472. dropbear_exit("Out of memory"); /* TODO disconnect */
  473. }
  474. pw = getpwnam(ses.authstate.pw_name);
  475. if (!pw)
  476. dropbear_exit("getpwnam failed after succeeding previously");
  477. pty_setowner(pw, chansess->tty);
  478. /* Set up the rows/col counts */
  479. sessionwinchange(chansess);
  480. /* Read the terminal modes */
  481. get_termmodes(chansess);
  482. TRACE(("leave sessionpty"))
  483. return DROPBEAR_SUCCESS;
  484. }
  485. #ifndef USE_VFORK
  486. static void make_connection_string(struct ChanSess *chansess) {
  487. char *local_ip, *local_port, *remote_ip, *remote_port;
  488. size_t len;
  489. get_socket_address(ses.sock_in, &local_ip, &local_port, &remote_ip, &remote_port, 0);
  490. /* "remoteip remoteport localip localport" */
  491. len = strlen(local_ip) + strlen(remote_ip) + 20;
  492. chansess->connection_string = m_malloc(len);
  493. snprintf(chansess->connection_string, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port);
  494. /* deprecated but bash only loads .bashrc if SSH_CLIENT is set */
  495. /* "remoteip remoteport localport" */
  496. len = strlen(remote_ip) + 20;
  497. chansess->client_string = m_malloc(len);
  498. snprintf(chansess->client_string, len, "%s %s %s", remote_ip, remote_port, local_port);
  499. m_free(local_ip);
  500. m_free(local_port);
  501. m_free(remote_ip);
  502. m_free(remote_port);
  503. }
  504. #endif
  505. /* Handle a command request from the client. This is used for both shell
  506. * and command-execution requests, and passes the command to
  507. * noptycommand or ptycommand as appropriate.
  508. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
  509. static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
  510. int iscmd, int issubsys) {
  511. unsigned int cmdlen;
  512. int ret;
  513. TRACE(("enter sessioncommand"))
  514. if (chansess->cmd != NULL) {
  515. /* Note that only one command can _succeed_. The client might try
  516. * one command (which fails), then try another. Ie fallback
  517. * from sftp to scp */
  518. return DROPBEAR_FAILURE;
  519. }
  520. if (iscmd) {
  521. /* "exec" */
  522. if (chansess->cmd == NULL) {
  523. chansess->cmd = buf_getstring(ses.payload, &cmdlen);
  524. if (cmdlen > MAX_CMD_LEN) {
  525. m_free(chansess->cmd);
  526. /* TODO - send error - too long ? */
  527. return DROPBEAR_FAILURE;
  528. }
  529. }
  530. if (issubsys) {
  531. #ifdef SFTPSERVER_PATH
  532. if ((cmdlen == 4) && strncmp(chansess->cmd, "sftp", 4) == 0) {
  533. m_free(chansess->cmd);
  534. chansess->cmd = m_strdup(SFTPSERVER_PATH);
  535. } else
  536. #endif
  537. {
  538. m_free(chansess->cmd);
  539. return DROPBEAR_FAILURE;
  540. }
  541. }
  542. }
  543. /* take public key option 'command' into account */
  544. svr_pubkey_set_forced_command(chansess);
  545. #ifdef LOG_COMMANDS
  546. if (chansess->cmd) {
  547. dropbear_log(LOG_INFO, "User %s executing '%s'",
  548. ses.authstate.pw_name, chansess->cmd);
  549. } else {
  550. dropbear_log(LOG_INFO, "User %s executing login shell",
  551. ses.authstate.pw_name);
  552. }
  553. #endif
  554. /* uClinux will vfork(), so there'll be a race as
  555. connection_string is freed below. */
  556. #ifndef USE_VFORK
  557. make_connection_string(chansess);
  558. #endif
  559. if (chansess->term == NULL) {
  560. /* no pty */
  561. ret = noptycommand(channel, chansess);
  562. if (ret == DROPBEAR_SUCCESS) {
  563. channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
  564. update_channel_prio();
  565. }
  566. } else {
  567. /* want pty */
  568. ret = ptycommand(channel, chansess);
  569. }
  570. #ifndef USE_VFORK
  571. m_free(chansess->connection_string);
  572. m_free(chansess->client_string);
  573. #endif
  574. if (ret == DROPBEAR_FAILURE) {
  575. m_free(chansess->cmd);
  576. }
  577. return ret;
  578. }
  579. /* Execute a command and set up redirection of stdin/stdout/stderr without a
  580. * pty.
  581. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
  582. static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
  583. int ret;
  584. TRACE(("enter noptycommand"))
  585. ret = spawn_command(execchild, chansess,
  586. &channel->writefd, &channel->readfd, &channel->errfd,
  587. &chansess->pid);
  588. if (ret == DROPBEAR_FAILURE) {
  589. return ret;
  590. }
  591. ses.maxfd = MAX(ses.maxfd, channel->writefd);
  592. ses.maxfd = MAX(ses.maxfd, channel->readfd);
  593. ses.maxfd = MAX(ses.maxfd, channel->errfd);
  594. addchildpid(chansess, chansess->pid);
  595. if (svr_ses.lastexit.exitpid != -1) {
  596. unsigned int i;
  597. TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
  598. /* The child probably exited and the signal handler triggered
  599. * possibly before we got around to adding the childpid. So we fill
  600. * out its data manually */
  601. for (i = 0; i < svr_ses.childpidsize; i++) {
  602. if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
  603. TRACE(("found match for lastexitpid"))
  604. svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
  605. svr_ses.lastexit.exitpid = -1;
  606. break;
  607. }
  608. }
  609. }
  610. TRACE(("leave noptycommand"))
  611. return DROPBEAR_SUCCESS;
  612. }
  613. /* Execute a command or shell within a pty environment, and set up
  614. * redirection as appropriate.
  615. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
  616. static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
  617. pid_t pid;
  618. struct logininfo *li = NULL;
  619. #ifdef DO_MOTD
  620. buffer * motdbuf = NULL;
  621. int len;
  622. struct stat sb;
  623. char *hushpath = NULL;
  624. #endif
  625. TRACE(("enter ptycommand"))
  626. /* we need to have a pty allocated */
  627. if (chansess->master == -1 || chansess->tty == NULL) {
  628. dropbear_log(LOG_WARNING, "No pty was allocated, couldn't execute");
  629. return DROPBEAR_FAILURE;
  630. }
  631. #ifdef USE_VFORK
  632. pid = vfork();
  633. #else
  634. pid = fork();
  635. #endif
  636. if (pid < 0)
  637. return DROPBEAR_FAILURE;
  638. if (pid == 0) {
  639. /* child */
  640. TRACE(("back to normal sigchld"))
  641. /* Revert to normal sigchld handling */
  642. if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
  643. dropbear_exit("signal() error");
  644. }
  645. /* redirect stdin/stdout/stderr */
  646. close(chansess->master);
  647. pty_make_controlling_tty(&chansess->slave, chansess->tty);
  648. if ((dup2(chansess->slave, STDIN_FILENO) < 0) ||
  649. (dup2(chansess->slave, STDERR_FILENO) < 0) ||
  650. (dup2(chansess->slave, STDOUT_FILENO) < 0)) {
  651. TRACE(("leave ptycommand: error redirecting filedesc"))
  652. return DROPBEAR_FAILURE;
  653. }
  654. close(chansess->slave);
  655. /* write the utmp/wtmp login record - must be after changing the
  656. * terminal used for stdout with the dup2 above */
  657. li = chansess_login_alloc(chansess);
  658. login_login(li);
  659. login_free_entry(li);
  660. #ifdef DO_MOTD
  661. if (svr_opts.domotd && !chansess->cmd) {
  662. /* don't show the motd if ~/.hushlogin exists */
  663. /* 12 == strlen("/.hushlogin\0") */
  664. len = strlen(ses.authstate.pw_dir) + 12;
  665. hushpath = m_malloc(len);
  666. snprintf(hushpath, len, "%s/.hushlogin", ses.authstate.pw_dir);
  667. if (stat(hushpath, &sb) < 0) {
  668. /* more than a screenful is stupid IMHO */
  669. motdbuf = buf_new(80 * 25);
  670. if (buf_readfile(motdbuf, MOTD_FILENAME) == DROPBEAR_SUCCESS) {
  671. buf_setpos(motdbuf, 0);
  672. while (motdbuf->pos != motdbuf->len) {
  673. len = motdbuf->len - motdbuf->pos;
  674. len = write(STDOUT_FILENO,
  675. buf_getptr(motdbuf, len), len);
  676. buf_incrpos(motdbuf, len);
  677. }
  678. }
  679. buf_free(motdbuf);
  680. }
  681. m_free(hushpath);
  682. }
  683. #endif /* DO_MOTD */
  684. execchild(chansess);
  685. /* not reached */
  686. } else {
  687. /* parent */
  688. TRACE(("continue ptycommand: parent"))
  689. chansess->pid = pid;
  690. /* add a child pid */
  691. addchildpid(chansess, pid);
  692. close(chansess->slave);
  693. channel->writefd = chansess->master;
  694. channel->readfd = chansess->master;
  695. /* don't need to set stderr here */
  696. ses.maxfd = MAX(ses.maxfd, chansess->master);
  697. setnonblocking(chansess->master);
  698. }
  699. TRACE(("leave ptycommand"))
  700. return DROPBEAR_SUCCESS;
  701. }
  702. /* Add the pid of a child to the list for exit-handling */
  703. static void addchildpid(struct ChanSess *chansess, pid_t pid) {
  704. unsigned int i;
  705. for (i = 0; i < svr_ses.childpidsize; i++) {
  706. if (svr_ses.childpids[i].pid == -1) {
  707. break;
  708. }
  709. }
  710. /* need to increase size */
  711. if (i == svr_ses.childpidsize) {
  712. svr_ses.childpids = (struct ChildPid*)m_realloc(svr_ses.childpids,
  713. sizeof(struct ChildPid) * (svr_ses.childpidsize+1));
  714. svr_ses.childpidsize++;
  715. }
  716. svr_ses.childpids[i].pid = pid;
  717. svr_ses.childpids[i].chansess = chansess;
  718. }
  719. /* Clean up, drop to user privileges, set up the environment and execute
  720. * the command/shell. This function does not return. */
  721. static void execchild(void *user_data) {
  722. struct ChanSess *chansess = user_data;
  723. char *usershell = NULL;
  724. /* with uClinux we'll have vfork()ed, so don't want to overwrite the
  725. * hostkey. can't think of a workaround to clear it */
  726. #ifndef USE_VFORK
  727. /* wipe the hostkey */
  728. sign_key_free(svr_opts.hostkey);
  729. svr_opts.hostkey = NULL;
  730. /* overwrite the prng state */
  731. seedrandom();
  732. #endif
  733. /* clear environment */
  734. /* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
  735. * etc. This is hazardous, so should only be used for debugging. */
  736. #ifndef DEBUG_VALGRIND
  737. #ifdef HAVE_CLEARENV
  738. clearenv();
  739. #else /* don't HAVE_CLEARENV */
  740. /* Yay for posix. */
  741. if (environ) {
  742. environ[0] = NULL;
  743. }
  744. #endif /* HAVE_CLEARENV */
  745. #endif /* DEBUG_VALGRIND */
  746. /* We can only change uid/gid as root ... */
  747. if (getuid() == 0) {
  748. if ((setgid(ses.authstate.pw_gid) < 0) ||
  749. (initgroups(ses.authstate.pw_name,
  750. ses.authstate.pw_gid) < 0)) {
  751. dropbear_exit("Error changing user group");
  752. }
  753. if (setuid(ses.authstate.pw_uid) < 0) {
  754. dropbear_exit("Error changing user");
  755. }
  756. } else {
  757. /* ... but if the daemon is the same uid as the requested uid, we don't
  758. * need to */
  759. /* XXX - there is a minor issue here, in that if there are multiple
  760. * usernames with the same uid, but differing groups, then the
  761. * differing groups won't be set (as with initgroups()). The solution
  762. * is for the sysadmin not to give out the UID twice */
  763. if (getuid() != ses.authstate.pw_uid) {
  764. dropbear_exit("Couldn't change user as non-root");
  765. }
  766. }
  767. /* set env vars */
  768. addnewvar("USER", ses.authstate.pw_name);
  769. addnewvar("LOGNAME", ses.authstate.pw_name);
  770. addnewvar("HOME", ses.authstate.pw_dir);
  771. addnewvar("SHELL", get_user_shell());
  772. addnewvar("PATH", DEFAULT_PATH);
  773. if (chansess->term != NULL) {
  774. addnewvar("TERM", chansess->term);
  775. }
  776. if (chansess->tty) {
  777. addnewvar("SSH_TTY", chansess->tty);
  778. }
  779. if (chansess->connection_string) {
  780. addnewvar("SSH_CONNECTION", chansess->connection_string);
  781. }
  782. if (chansess->client_string) {
  783. addnewvar("SSH_CLIENT", chansess->client_string);
  784. }
  785. #ifdef ENABLE_SVR_PUBKEY_OPTIONS
  786. if (chansess->original_command) {
  787. addnewvar("SSH_ORIGINAL_COMMAND", chansess->original_command);
  788. }
  789. #endif
  790. /* change directory */
  791. if (chdir(ses.authstate.pw_dir) < 0) {
  792. dropbear_exit("Error changing directory");
  793. }
  794. #ifndef DISABLE_X11FWD
  795. /* set up X11 forwarding if enabled */
  796. x11setauth(chansess);
  797. #endif
  798. #ifdef ENABLE_SVR_AGENTFWD
  799. /* set up agent env variable */
  800. svr_agentset(chansess);
  801. #endif
  802. usershell = m_strdup(get_user_shell());
  803. run_shell_command(chansess->cmd, ses.maxfd, usershell);
  804. /* only reached on error */
  805. dropbear_exit("Child failed");
  806. }
  807. /* Set up the general chansession environment, in particular child-exit
  808. * handling */
  809. void svr_chansessinitialise() {
  810. struct sigaction sa_chld;
  811. /* single child process intially */
  812. svr_ses.childpids = (struct ChildPid*)m_malloc(sizeof(struct ChildPid));
  813. svr_ses.childpids[0].pid = -1; /* unused */
  814. svr_ses.childpids[0].chansess = NULL;
  815. svr_ses.childpidsize = 1;
  816. svr_ses.lastexit.exitpid = -1; /* Nothing has exited yet */
  817. sa_chld.sa_handler = sesssigchild_handler;
  818. sa_chld.sa_flags = SA_NOCLDSTOP;
  819. sigemptyset(&sa_chld.sa_mask);
  820. if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
  821. dropbear_exit("signal() error");
  822. }
  823. }
  824. /* add a new environment variable, allocating space for the entry */
  825. void addnewvar(const char* param, const char* var) {
  826. char* newvar = NULL;
  827. int plen, vlen;
  828. plen = strlen(param);
  829. vlen = strlen(var);
  830. newvar = m_malloc(plen + vlen + 2); /* 2 is for '=' and '\0' */
  831. memcpy(newvar, param, plen);
  832. newvar[plen] = '=';
  833. memcpy(&newvar[plen+1], var, vlen);
  834. newvar[plen+vlen+1] = '\0';
  835. /* newvar is leaked here, but that's part of putenv()'s semantics */
  836. if (putenv(newvar) < 0) {
  837. dropbear_exit("environ error");
  838. }
  839. }