session.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. * session.c - PPP session control.
  3. *
  4. * Copyright (c) 2007 Diego Rivera. All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. *
  13. * 2. The name(s) of the authors of this software must not be used to
  14. * endorse or promote products derived from this software without
  15. * prior written permission.
  16. *
  17. * 3. Redistributions of any form whatsoever must retain the following
  18. * acknowledgment:
  19. * "This product includes software developed by Paul Mackerras
  20. * <paulus@samba.org>".
  21. *
  22. * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
  23. * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  24. * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
  25. * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  26. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  27. * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  28. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  29. *
  30. * Derived from auth.c, which is:
  31. *
  32. * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
  33. *
  34. * Redistribution and use in source and binary forms, with or without
  35. * modification, are permitted provided that the following conditions
  36. * are met:
  37. *
  38. * 1. Redistributions of source code must retain the above copyright
  39. * notice, this list of conditions and the following disclaimer.
  40. *
  41. * 2. Redistributions in binary form must reproduce the above copyright
  42. * notice, this list of conditions and the following disclaimer in
  43. * the documentation and/or other materials provided with the
  44. * distribution.
  45. *
  46. * 3. The name "Carnegie Mellon University" must not be used to
  47. * endorse or promote products derived from this software without
  48. * prior written permission. For permission or any legal
  49. * details, please contact
  50. * Office of Technology Transfer
  51. * Carnegie Mellon University
  52. * 5000 Forbes Avenue
  53. * Pittsburgh, PA 15213-3890
  54. * (412) 268-4387, fax: (412) 268-7395
  55. * tech-transfer@andrew.cmu.edu
  56. *
  57. * 4. Redistributions of any form whatsoever must retain the following
  58. * acknowledgment:
  59. * "This product includes software developed by Computing Services
  60. * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
  61. *
  62. * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
  63. * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  64. * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
  65. * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  66. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  67. * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  68. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  69. */
  70. #include <stdio.h>
  71. #include <stdlib.h>
  72. #include <string.h>
  73. #include <pwd.h>
  74. #include <crypt.h>
  75. #ifdef HAS_SHADOW
  76. #include <shadow.h>
  77. #endif
  78. #include <time.h>
  79. #include <utmp.h>
  80. #include <fcntl.h>
  81. #include <unistd.h>
  82. #include "pppd.h"
  83. #include "session.h"
  84. #ifdef USE_PAM
  85. #include <security/pam_appl.h>
  86. #endif /* #ifdef USE_PAM */
  87. #define SET_MSG(var, msg) if (var != NULL) { var[0] = msg; }
  88. #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
  89. #define SUCCESS_MSG "Session started successfully"
  90. #define ABORT_MSG "Session can't be started without a username"
  91. #define SERVICE_NAME "ppp"
  92. #define SESSION_FAILED 0
  93. #define SESSION_OK 1
  94. /* We have successfully started a session */
  95. static bool logged_in = 0;
  96. #ifdef USE_PAM
  97. /*
  98. * Static variables used to communicate between the conversation function
  99. * and the server_login function
  100. */
  101. static const char *PAM_username;
  102. static const char *PAM_password;
  103. static int PAM_session = 0;
  104. static pam_handle_t *pamh = NULL;
  105. /* PAM conversation function
  106. * Here we assume (for now, at least) that echo on means login name, and
  107. * echo off means password.
  108. */
  109. static int conversation (int num_msg,
  110. #ifndef SOL2
  111. const
  112. #endif
  113. struct pam_message **msg,
  114. struct pam_response **resp, void *appdata_ptr)
  115. {
  116. int replies = 0;
  117. struct pam_response *reply = NULL;
  118. reply = malloc(sizeof(struct pam_response) * num_msg);
  119. if (!reply) return PAM_CONV_ERR;
  120. for (replies = 0; replies < num_msg; replies++) {
  121. switch (msg[replies]->msg_style) {
  122. case PAM_PROMPT_ECHO_ON:
  123. reply[replies].resp_retcode = PAM_SUCCESS;
  124. reply[replies].resp = COPY_STRING(PAM_username);
  125. /* PAM frees resp */
  126. break;
  127. case PAM_PROMPT_ECHO_OFF:
  128. reply[replies].resp_retcode = PAM_SUCCESS;
  129. reply[replies].resp = COPY_STRING(PAM_password);
  130. /* PAM frees resp */
  131. break;
  132. case PAM_TEXT_INFO:
  133. /* fall through */
  134. case PAM_ERROR_MSG:
  135. /* ignore it, but pam still wants a NULL response... */
  136. reply[replies].resp_retcode = PAM_SUCCESS;
  137. reply[replies].resp = NULL;
  138. break;
  139. default:
  140. /* Must be an error of some sort... */
  141. free (reply);
  142. return PAM_CONV_ERR;
  143. }
  144. }
  145. *resp = reply;
  146. return PAM_SUCCESS;
  147. }
  148. static struct pam_conv pam_conv_data = {
  149. &conversation,
  150. NULL
  151. };
  152. #endif /* #ifdef USE_PAM */
  153. int
  154. session_start(flags, user, passwd, ttyName, msg)
  155. const int flags;
  156. const char *user;
  157. const char *passwd;
  158. const char *ttyName;
  159. char **msg;
  160. {
  161. #ifdef USE_PAM
  162. bool ok = 1;
  163. const char *usr;
  164. int pam_error;
  165. bool try_session = 0;
  166. #else /* #ifdef USE_PAM */
  167. struct passwd *pw;
  168. char *cbuf;
  169. #ifdef HAS_SHADOW
  170. struct spwd *spwd;
  171. struct spwd *getspnam();
  172. long now = 0;
  173. #endif /* #ifdef HAS_SHADOW */
  174. #endif /* #ifdef USE_PAM */
  175. SET_MSG(msg, SUCCESS_MSG);
  176. /* If no verification is requested, then simply return an OK */
  177. if (!(SESS_ALL & flags)) {
  178. return SESSION_OK;
  179. }
  180. if (user == NULL) {
  181. SET_MSG(msg, ABORT_MSG);
  182. return SESSION_FAILED;
  183. }
  184. #ifdef USE_PAM
  185. /* Find the '\\' in the username */
  186. /* This needs to be fixed to support different username schemes */
  187. if ((usr = strchr(user, '\\')) == NULL)
  188. usr = user;
  189. else
  190. usr++;
  191. PAM_session = 0;
  192. PAM_username = usr;
  193. PAM_password = passwd;
  194. dbglog("Initializing PAM (%d) for user %s", flags, usr);
  195. pam_error = pam_start (SERVICE_NAME, usr, &pam_conv_data, &pamh);
  196. dbglog("---> PAM INIT Result = %d", pam_error);
  197. ok = (pam_error == PAM_SUCCESS);
  198. if (ok) {
  199. ok = (pam_set_item(pamh, PAM_TTY, ttyName) == PAM_SUCCESS) &&
  200. (pam_set_item(pamh, PAM_RHOST, ifname) == PAM_SUCCESS);
  201. }
  202. if (ok && (SESS_AUTH & flags)) {
  203. dbglog("Attempting PAM authentication");
  204. pam_error = pam_authenticate (pamh, PAM_SILENT);
  205. if (pam_error == PAM_SUCCESS) {
  206. /* PAM auth was OK */
  207. dbglog("PAM Authentication OK for %s", user);
  208. } else {
  209. /* No matter the reason, we fail because we're authenticating */
  210. ok = 0;
  211. if (pam_error == PAM_USER_UNKNOWN) {
  212. dbglog("User unknown, failing PAM authentication");
  213. SET_MSG(msg, "User unknown - cannot authenticate via PAM");
  214. } else {
  215. /* Any other error means authentication was bad */
  216. dbglog("PAM Authentication failed: %d: %s", pam_error,
  217. pam_strerror(pamh, pam_error));
  218. SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
  219. }
  220. }
  221. }
  222. if (ok && (SESS_ACCT & flags)) {
  223. dbglog("Attempting PAM account checks");
  224. pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
  225. if (pam_error == PAM_SUCCESS) {
  226. /*
  227. * PAM account was OK, set the flag which indicates that we should
  228. * try to perform the session checks.
  229. */
  230. try_session = 1;
  231. dbglog("PAM Account OK for %s", user);
  232. } else {
  233. /*
  234. * If the account checks fail, then we should not try to perform
  235. * the session check, because they don't make sense.
  236. */
  237. try_session = 0;
  238. if (pam_error == PAM_USER_UNKNOWN) {
  239. /*
  240. * We're checking the account, so it's ok to not have one
  241. * because the user might come from the secrets files, or some
  242. * other plugin.
  243. */
  244. dbglog("User unknown, ignoring PAM restrictions");
  245. SET_MSG(msg, "User unknown - ignoring PAM restrictions");
  246. } else {
  247. /* Any other error means session is rejected */
  248. ok = 0;
  249. dbglog("PAM Account checks failed: %d: %s", pam_error,
  250. pam_strerror(pamh, pam_error));
  251. SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
  252. }
  253. }
  254. }
  255. if (ok && try_session && (SESS_ACCT & flags)) {
  256. /* Only open a session if the user's account was found */
  257. pam_error = pam_open_session (pamh, PAM_SILENT);
  258. if (pam_error == PAM_SUCCESS) {
  259. dbglog("PAM Session opened for user %s", user);
  260. PAM_session = 1;
  261. } else {
  262. dbglog("PAM Session denied for user %s", user);
  263. SET_MSG(msg, (char *) pam_strerror (pamh, pam_error));
  264. ok = 0;
  265. }
  266. }
  267. /* This is needed because apparently the PAM stuff closes the log */
  268. reopen_log();
  269. /* If our PAM checks have already failed, then we must return a failure */
  270. if (!ok) return SESSION_FAILED;
  271. #else /* #ifdef USE_PAM */
  272. /*
  273. * Use the non-PAM methods directly. 'pw' will remain NULL if the user
  274. * has not been authenticated using local UNIX system services.
  275. */
  276. pw = NULL;
  277. if ((SESS_AUTH & flags)) {
  278. pw = getpwnam(user);
  279. endpwent();
  280. /*
  281. * Here, we bail if we have no user account, because there is nothing
  282. * to verify against.
  283. */
  284. if (pw == NULL)
  285. return SESSION_FAILED;
  286. #ifdef HAS_SHADOW
  287. spwd = getspnam(user);
  288. endspent();
  289. /*
  290. * If there is no shadow entry for the user, then we can't verify the
  291. * account.
  292. */
  293. if (spwd == NULL)
  294. return SESSION_FAILED;
  295. /*
  296. * We check validity all the time, because if the password has expired,
  297. * then clearly we should not authenticate against it (if we're being
  298. * called for authentication only). Thus, in this particular instance,
  299. * there is no real difference between using the AUTH, SESS or ACCT
  300. * flags, or combinations thereof.
  301. */
  302. now = time(NULL) / 86400L;
  303. if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
  304. || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
  305. && spwd->sp_lstchg >= 0
  306. && now >= spwd->sp_lstchg + spwd->sp_max)) {
  307. warn("Password for %s has expired", user);
  308. return SESSION_FAILED;
  309. }
  310. /* We have a valid shadow entry, keep the password */
  311. pw->pw_passwd = spwd->sp_pwdp;
  312. #endif /* #ifdef HAS_SHADOW */
  313. /*
  314. * If no passwd, don't let them login if we're authenticating.
  315. */
  316. if (pw->pw_passwd == NULL || strlen(pw->pw_passwd) < 2)
  317. return SESSION_FAILED;
  318. cbuf = crypt(passwd, pw->pw_passwd);
  319. if (!cbuf || strcmp(cbuf, pw->pw_passwd) != 0)
  320. return SESSION_FAILED;
  321. }
  322. #endif /* #ifdef USE_PAM */
  323. /*
  324. * Write a wtmp entry for this user.
  325. */
  326. if (SESS_ACCT & flags) {
  327. if (strncmp(ttyName, "/dev/", 5) == 0)
  328. ttyName += 5;
  329. logwtmp(ttyName, user, ifname); /* Add wtmp login entry */
  330. logged_in = 1;
  331. #if defined(_PATH_LASTLOG) && !defined(USE_PAM)
  332. /*
  333. * Enter the user in lastlog only if he has been authenticated using
  334. * local system services. If he has not, then we don't know what his
  335. * UID might be, and lastlog is indexed by UID.
  336. */
  337. if (pw != NULL) {
  338. struct lastlog ll;
  339. int fd;
  340. time_t tnow;
  341. if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
  342. (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
  343. memset((void *)&ll, 0, sizeof(ll));
  344. (void)time(&tnow);
  345. ll.ll_time = tnow;
  346. (void)strncpy(ll.ll_line, ttyName, sizeof(ll.ll_line));
  347. (void)strncpy(ll.ll_host, ifname, sizeof(ll.ll_host));
  348. (void)write(fd, (char *)&ll, sizeof(ll));
  349. (void)close(fd);
  350. }
  351. }
  352. #endif /* _PATH_LASTLOG and not USE_PAM */
  353. info("user %s logged in on tty %s intf %s", user, ttyName, ifname);
  354. }
  355. return SESSION_OK;
  356. }
  357. /*
  358. * session_end - Logout the user.
  359. */
  360. void
  361. session_end(const char* ttyName)
  362. {
  363. #ifdef USE_PAM
  364. int pam_error = PAM_SUCCESS;
  365. if (pamh != NULL) {
  366. if (PAM_session) pam_error = pam_close_session (pamh, PAM_SILENT);
  367. PAM_session = 0;
  368. pam_end (pamh, pam_error);
  369. pamh = NULL;
  370. /* Apparently the pam stuff does closelog(). */
  371. reopen_log();
  372. }
  373. #endif
  374. if (logged_in) {
  375. if (strncmp(ttyName, "/dev/", 5) == 0)
  376. ttyName += 5;
  377. logwtmp(ttyName, "", ""); /* Wipe out utmp logout entry */
  378. logged_in = 0;
  379. }
  380. }