svr-chansession.c 29 KB

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