winbind.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. /***********************************************************************
  2. *
  3. * winbind.c
  4. *
  5. * WINBIND plugin for pppd. Performs PAP, CHAP, MS-CHAP, MS-CHAPv2
  6. * authentication using WINBIND to contact a NT-style PDC.
  7. *
  8. * Based on the structure of the radius module.
  9. *
  10. * Copyright (C) 2003 Andrew Bartlet <abartlet@samba.org>
  11. *
  12. * Copyright 1999 Paul Mackerras, Alan Curry.
  13. * (pipe read code from passpromt.c)
  14. *
  15. * Copyright (C) 2002 Roaring Penguin Software Inc.
  16. *
  17. * Based on a patch for ipppd, which is:
  18. * Copyright (C) 1996, Matjaz Godec <gody@elgo.si>
  19. * Copyright (C) 1996, Lars Fenneberg <in5y050@public.uni-hamburg.de>
  20. * Copyright (C) 1997, Miguel A.L. Paraz <map@iphil.net>
  21. *
  22. * Uses radiusclient library, which is:
  23. * Copyright (C) 1995,1996,1997,1998 Lars Fenneberg <lf@elemental.net>
  24. * Copyright (C) 2002 Roaring Penguin Software Inc.
  25. *
  26. * MPPE support is by Ralf Hofmann, <ralf.hofmann@elvido.net>, with
  27. * modification from Frank Cusack, <frank@google.com>.
  28. *
  29. * Updated on 2003-12-12 to support updated PPP plugin API from latest CVS
  30. * Copyright (C) 2003, Sean E. Millichamp <sean at bruenor dot org>
  31. *
  32. * This plugin may be distributed according to the terms of the GNU
  33. * General Public License, version 2 or (at your option) any later version.
  34. *
  35. ***********************************************************************/
  36. #include "pppd.h"
  37. #include "chap-new.h"
  38. #include "chap_ms.h"
  39. #ifdef MPPE
  40. #include "md5.h"
  41. #endif
  42. #include "fsm.h"
  43. #include "ipcp.h"
  44. #include <syslog.h>
  45. #include <sys/types.h>
  46. #include <sys/stat.h>
  47. #include <fcntl.h>
  48. #include <sys/time.h>
  49. #include <sys/wait.h>
  50. #include <string.h>
  51. #include <unistd.h>
  52. #include <stdlib.h>
  53. #include <errno.h>
  54. #include <ctype.h>
  55. #define BUF_LEN 1024
  56. #define NOT_AUTHENTICATED 0
  57. #define AUTHENTICATED 1
  58. static char *ntlm_auth = NULL;
  59. static int set_ntlm_auth(char **argv)
  60. {
  61. char *p;
  62. p = argv[0];
  63. if (p[0] != '/') {
  64. option_error("ntlm_auth-helper argument must be full path");
  65. return 0;
  66. }
  67. p = strdup(p);
  68. if (p == NULL) {
  69. novm("ntlm_auth-helper argument");
  70. return 0;
  71. }
  72. if (ntlm_auth != NULL)
  73. free(ntlm_auth);
  74. ntlm_auth = p;
  75. return 1;
  76. }
  77. static option_t Options[] = {
  78. { "ntlm_auth-helper", o_special, (void *) &set_ntlm_auth,
  79. "Path to ntlm_auth executable", OPT_PRIV },
  80. { NULL }
  81. };
  82. static int
  83. winbind_secret_check(void);
  84. static int winbind_pap_auth(char *user,
  85. char *passwd,
  86. char **msgp,
  87. struct wordlist **paddrs,
  88. struct wordlist **popts);
  89. static int winbind_chap_verify(char *user, char *ourname, int id,
  90. struct chap_digest_type *digest,
  91. unsigned char *challenge,
  92. unsigned char *response,
  93. char *message, int message_space);
  94. static int winbind_allowed_address(u_int32_t addr);
  95. char pppd_version[] = VERSION;
  96. /**********************************************************************
  97. * %FUNCTION: plugin_init
  98. * %ARGUMENTS:
  99. * None
  100. * %RETURNS:
  101. * Nothing
  102. * %DESCRIPTION:
  103. * Initializes WINBIND plugin.
  104. ***********************************************************************/
  105. void
  106. plugin_init(void)
  107. {
  108. pap_check_hook = winbind_secret_check;
  109. pap_auth_hook = winbind_pap_auth;
  110. chap_check_hook = winbind_secret_check;
  111. chap_verify_hook = winbind_chap_verify;
  112. allowed_address_hook = winbind_allowed_address;
  113. /* Don't ask the peer for anything other than MS-CHAP or MS-CHAP V2 */
  114. chap_mdtype_all &= (MDTYPE_MICROSOFT_V2 | MDTYPE_MICROSOFT);
  115. add_options(Options);
  116. info("WINBIND plugin initialized.");
  117. }
  118. /**
  119. Routine to get hex characters and turn them into a 16 byte array.
  120. the array can be variable length, and any non-hex-numeric
  121. characters are skipped. "0xnn" or "0Xnn" is specially catered
  122. for.
  123. valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n"
  124. **/
  125. /*
  126. Unix SMB/CIFS implementation.
  127. Samba utility functions
  128. Copyright (C) Andrew Tridgell 1992-2001
  129. Copyright (C) Simo Sorce 2001-2002
  130. Copyright (C) Martin Pool 2003
  131. This program is free software; you can redistribute it and/or modify
  132. it under the terms of the GNU General Public License as published by
  133. the Free Software Foundation; either version 2 of the License, or
  134. (at your option) any later version.
  135. This program is distributed in the hope that it will be useful,
  136. but WITHOUT ANY WARRANTY; without even the implied warranty of
  137. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  138. GNU General Public License for more details.
  139. You should have received a copy of the GNU General Public License
  140. along with this program; if not, write to the Free Software
  141. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  142. */
  143. size_t strhex_to_str(char *p, size_t len, const char *strhex)
  144. {
  145. size_t i;
  146. size_t num_chars = 0;
  147. unsigned char lonybble, hinybble;
  148. const char *hexchars = "0123456789ABCDEF";
  149. char *p1 = NULL, *p2 = NULL;
  150. for (i = 0; i < len && strhex[i] != 0; i++) {
  151. if (strncmp(hexchars, "0x", 2) == 0) {
  152. i++; /* skip two chars */
  153. continue;
  154. }
  155. if (!(p1 = strchr(hexchars, toupper(strhex[i]))))
  156. break;
  157. i++; /* next hex digit */
  158. if (!(p2 = strchr(hexchars, toupper(strhex[i]))))
  159. break;
  160. /* get the two nybbles */
  161. hinybble = (p1 - hexchars);
  162. lonybble = (p2 - hexchars);
  163. p[num_chars] = (hinybble << 4) | lonybble;
  164. num_chars++;
  165. p1 = NULL;
  166. p2 = NULL;
  167. }
  168. return num_chars;
  169. }
  170. static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  171. /**
  172. * Encode a base64 string into a malloc()ed string caller to free.
  173. *
  174. *From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments
  175. **/
  176. char * base64_encode(const char *data)
  177. {
  178. size_t out_cnt = 0;
  179. size_t len = strlen(data);
  180. size_t output_len = 4 * ((len + 2) / 3) + 2;
  181. const unsigned char *ptr = (const unsigned char *) data;
  182. char *result = malloc(output_len); /* get us plenty of space */
  183. unsigned int bits;
  184. for (; len >= 3; len -= 3) {
  185. bits = (ptr[0] << 16) + (ptr[1] << 8) + ptr[2];
  186. ptr += 3;
  187. result[out_cnt++] = b64[bits >> 18];
  188. result[out_cnt++] = b64[(bits >> 12) & 0x3f];
  189. result[out_cnt++] = b64[(bits >> 6) & 0x3f];
  190. result[out_cnt++] = b64[bits & 0x3f];
  191. }
  192. if (len != 0) {
  193. bits = ptr[0] << 16;
  194. if (len > 1)
  195. bits |= ptr[1] << 8;
  196. result[out_cnt++] = b64[bits >> 18];
  197. result[out_cnt++] = b64[(bits >> 12) & 0x3f];
  198. result[out_cnt++] = (len > 1)? b64[(bits >> 6) & 0x3f]: '=';
  199. result[out_cnt++] = '=';
  200. }
  201. result[out_cnt] = '\0'; /* terminate */
  202. return result;
  203. }
  204. unsigned int run_ntlm_auth(const char *username,
  205. const char *domain,
  206. const char *full_username,
  207. const char *plaintext_password,
  208. const u_char *challenge,
  209. size_t challenge_length,
  210. const u_char *lm_response,
  211. size_t lm_response_length,
  212. const u_char *nt_response,
  213. size_t nt_response_length,
  214. u_char nt_key[16],
  215. char **error_string)
  216. {
  217. pid_t forkret;
  218. int child_in[2];
  219. int child_out[2];
  220. int status;
  221. int authenticated = NOT_AUTHENTICATED; /* not auth */
  222. int got_user_session_key = 0; /* not got key */
  223. char buffer[1024];
  224. FILE *pipe_in;
  225. FILE *pipe_out;
  226. int i;
  227. char *challenge_hex;
  228. char *lm_hex_hash;
  229. char *nt_hex_hash;
  230. /* First see if we have a program to run... */
  231. if (ntlm_auth == NULL)
  232. return NOT_AUTHENTICATED;
  233. /* Make first child */
  234. if (pipe(child_out) == -1) {
  235. error("pipe creation failed for child OUT!");
  236. return NOT_AUTHENTICATED;
  237. }
  238. if (pipe(child_in) == -1) {
  239. error("pipe creation failed for child IN!");
  240. return NOT_AUTHENTICATED;
  241. }
  242. forkret = safe_fork(child_in[0], child_out[1], 2);
  243. if (forkret == -1) {
  244. if (error_string) {
  245. *error_string = strdup("fork failed!");
  246. }
  247. return NOT_AUTHENTICATED;
  248. }
  249. if (forkret == 0) {
  250. /* child process */
  251. uid_t uid;
  252. close(child_out[0]);
  253. close(child_in[1]);
  254. /* run winbind as the user that invoked pppd */
  255. setgid(getgid());
  256. uid = getuid();
  257. if (setuid(uid) == -1 || getuid() != uid)
  258. fatal("pppd/winbind: could not setuid to %d: %m", uid);
  259. execl("/bin/sh", "sh", "-c", ntlm_auth, NULL);
  260. fatal("pppd/winbind: could not exec /bin/sh: %m");
  261. }
  262. /* parent */
  263. close(child_out[1]);
  264. close(child_in[0]);
  265. /* Need to write the User's info onto the pipe */
  266. pipe_in = fdopen(child_in[1], "w");
  267. pipe_out = fdopen(child_out[0], "r");
  268. /* look for session key coming back */
  269. if (username) {
  270. char *b64_username = base64_encode(username);
  271. fprintf(pipe_in, "Username:: %s\n", b64_username);
  272. free(b64_username);
  273. }
  274. if (domain) {
  275. char *b64_domain = base64_encode(domain);
  276. fprintf(pipe_in, "NT-Domain:: %s\n", b64_domain);
  277. free(b64_domain);
  278. }
  279. if (full_username) {
  280. char *b64_full_username = base64_encode(full_username);
  281. fprintf(pipe_in, "Full-Username:: %s\n", b64_full_username);
  282. free(b64_full_username);
  283. }
  284. if (plaintext_password) {
  285. char *b64_plaintext_password = base64_encode(plaintext_password);
  286. fprintf(pipe_in, "Password:: %s\n", b64_plaintext_password);
  287. free(b64_plaintext_password);
  288. }
  289. if (challenge_length) {
  290. fprintf(pipe_in, "Request-User-Session-Key: yes\n");
  291. challenge_hex = malloc(challenge_length*2+1);
  292. for (i = 0; i < challenge_length; i++)
  293. sprintf(challenge_hex + i * 2, "%02X", challenge[i]);
  294. fprintf(pipe_in, "LANMAN-Challenge: %s\n", challenge_hex);
  295. free(challenge_hex);
  296. }
  297. if (lm_response_length) {
  298. lm_hex_hash = malloc(lm_response_length*2+1);
  299. for (i = 0; i < lm_response_length; i++)
  300. sprintf(lm_hex_hash + i * 2, "%02X", lm_response[i]);
  301. fprintf(pipe_in, "LANMAN-response: %s\n", lm_hex_hash);
  302. free(lm_hex_hash);
  303. }
  304. if (nt_response_length) {
  305. nt_hex_hash = malloc(nt_response_length*2+1);
  306. for (i = 0; i < nt_response_length; i++)
  307. sprintf(nt_hex_hash + i * 2, "%02X", nt_response[i]);
  308. fprintf(pipe_in, "NT-response: %s\n", nt_hex_hash);
  309. free(nt_hex_hash);
  310. }
  311. fprintf(pipe_in, ".\n");
  312. fflush(pipe_in);
  313. while (fgets(buffer, sizeof(buffer)-1, pipe_out) != NULL) {
  314. char *message, *parameter;
  315. if (buffer[strlen(buffer)-1] != '\n') {
  316. break;
  317. }
  318. buffer[strlen(buffer)-1] = '\0';
  319. message = buffer;
  320. if (!(parameter = strstr(buffer, ": "))) {
  321. break;
  322. }
  323. parameter[0] = '\0';
  324. parameter++;
  325. parameter[0] = '\0';
  326. parameter++;
  327. if (strcmp(message, ".") == 0) {
  328. /* end of sequence */
  329. break;
  330. } else if (strcasecmp(message, "Authenticated") == 0) {
  331. if (strcasecmp(parameter, "Yes") == 0) {
  332. authenticated = AUTHENTICATED;
  333. } else {
  334. notice("Winbind has declined authentication for user!");
  335. authenticated = NOT_AUTHENTICATED;
  336. }
  337. } else if (strcasecmp(message, "User-session-key") == 0) {
  338. /* length is the number of characters to parse */
  339. if (nt_key) {
  340. if (strhex_to_str(nt_key, 32, parameter) == 16) {
  341. got_user_session_key = 1;
  342. } else {
  343. notice("NT session key for user was not 16 bytes!");
  344. }
  345. }
  346. } else if (strcasecmp(message, "Error") == 0) {
  347. authenticated = NOT_AUTHENTICATED;
  348. if (error_string)
  349. *error_string = strdup(parameter);
  350. } else if (strcasecmp(message, "Authentication-Error") == 0) {
  351. authenticated = NOT_AUTHENTICATED;
  352. if (error_string)
  353. *error_string = strdup(parameter);
  354. } else {
  355. notice("unrecognised input from ntlm_auth helper - %s: %s", message, parameter);
  356. }
  357. }
  358. /* parent */
  359. if (close(child_out[0]) == -1) {
  360. notice("error closing pipe?!? for child OUT[0]");
  361. return NOT_AUTHENTICATED;
  362. }
  363. /* parent */
  364. if (close(child_in[1]) == -1) {
  365. notice("error closing pipe?!? for child IN[1]");
  366. return NOT_AUTHENTICATED;
  367. }
  368. while ((wait(&status) == -1) && errno == EINTR)
  369. ;
  370. if ((authenticated == AUTHENTICATED) && nt_key && !got_user_session_key) {
  371. notice("Did not get user session key, despite being authenticated!");
  372. return NOT_AUTHENTICATED;
  373. }
  374. return authenticated;
  375. }
  376. /**********************************************************************
  377. * %FUNCTION: winbind_secret_check
  378. * %ARGUMENTS:
  379. * None
  380. * %RETURNS:
  381. * 0 if we don't have an ntlm_auth program to run, otherwise 1.
  382. * %DESCRIPTION:
  383. * Tells pppd that we will try to authenticate the peer, and not to
  384. * worry about looking in /etc/ppp/ *-secrets
  385. ***********************************************************************/
  386. static int
  387. winbind_secret_check(void)
  388. {
  389. return ntlm_auth != NULL;
  390. }
  391. /**********************************************************************
  392. * %FUNCTION: winbind_pap_auth
  393. * %ARGUMENTS:
  394. * user -- user-name of peer
  395. * passwd -- password supplied by peer
  396. * msgp -- Message which will be sent in PAP response
  397. * paddrs -- set to a list of possible peer IP addresses
  398. * popts -- set to a list of additional pppd options
  399. * %RETURNS:
  400. * 1 if we can authenticate, -1 if we cannot.
  401. * %DESCRIPTION:
  402. * Performs PAP authentication using WINBIND
  403. ***********************************************************************/
  404. static int
  405. winbind_pap_auth(char *user,
  406. char *password,
  407. char **msgp,
  408. struct wordlist **paddrs,
  409. struct wordlist **popts)
  410. {
  411. if (run_ntlm_auth(NULL, NULL, user, password, NULL, 0, NULL, 0, NULL, 0, NULL, msgp) == AUTHENTICATED) {
  412. return 1;
  413. }
  414. return -1;
  415. }
  416. /**********************************************************************
  417. * %FUNCTION: winbind_chap_auth
  418. * %ARGUMENTS:
  419. * user -- user-name of peer
  420. * remmd -- hash received from peer
  421. * remmd_len -- length of remmd
  422. * cstate -- pppd's chap_state structure
  423. * %RETURNS:
  424. * AUTHENTICATED (1) if we can authenticate, NOT_AUTHENTICATED (0) if we cannot.
  425. * %DESCRIPTION:
  426. * Performs MS-CHAP and MS-CHAPv2 authentication using WINBIND.
  427. ***********************************************************************/
  428. static int
  429. winbind_chap_verify(char *user, char *ourname, int id,
  430. struct chap_digest_type *digest,
  431. unsigned char *challenge,
  432. unsigned char *response,
  433. char *message, int message_space)
  434. {
  435. int challenge_len, response_len;
  436. char domainname[256];
  437. char *domain;
  438. char *username;
  439. char *p;
  440. char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
  441. /* The first byte of each of these strings contains their length */
  442. challenge_len = *challenge++;
  443. response_len = *response++;
  444. /* remove domain from "domain\username" */
  445. if ((username = strrchr(user, '\\')) != NULL)
  446. ++username;
  447. else
  448. username = user;
  449. strlcpy(domainname, user, sizeof(domainname));
  450. /* remove domain from "domain\username" */
  451. if ((p = strrchr(domainname, '\\')) != NULL) {
  452. *p = '\0';
  453. domain = domainname;
  454. } else {
  455. domain = NULL;
  456. }
  457. /* generate MD based on negotiated type */
  458. switch (digest->code) {
  459. case CHAP_MICROSOFT:
  460. {
  461. char *error_string = NULL;
  462. u_char *nt_response = NULL;
  463. u_char *lm_response = NULL;
  464. int nt_response_size = 0;
  465. int lm_response_size = 0;
  466. u_char session_key[16];
  467. if (response_len != MS_CHAP_RESPONSE_LEN)
  468. break; /* not even the right length */
  469. /* Determine which part of response to verify against */
  470. if (response[MS_CHAP_USENT]) {
  471. nt_response = &response[MS_CHAP_NTRESP];
  472. nt_response_size = MS_CHAP_NTRESP_LEN;
  473. } else {
  474. #ifdef MSLANMAN
  475. lm_response = &response[MS_CHAP_LANMANRESP];
  476. lm_response_size = MS_CHAP_LANMANRESP_LEN;
  477. #else
  478. /* Should really propagate this into the error packet. */
  479. notice("Peer request for LANMAN auth not supported");
  480. return NOT_AUTHENTICATED;
  481. #endif /* MSLANMAN */
  482. }
  483. /* ship off to winbind, and check */
  484. if (run_ntlm_auth(username,
  485. domain,
  486. NULL,
  487. NULL,
  488. challenge, challenge_len,
  489. lm_response, lm_response_size,
  490. nt_response, nt_response_size,
  491. session_key,
  492. &error_string) == AUTHENTICATED) {
  493. mppe_set_keys(challenge, session_key);
  494. slprintf(message, message_space, "Access granted");
  495. return AUTHENTICATED;
  496. } else {
  497. if (error_string) {
  498. notice(error_string);
  499. free(error_string);
  500. }
  501. slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
  502. challenge_len, challenge);
  503. return NOT_AUTHENTICATED;
  504. }
  505. break;
  506. }
  507. case CHAP_MICROSOFT_V2:
  508. {
  509. u_char Challenge[8];
  510. u_char session_key[MD4_SIGNATURE_SIZE];
  511. char *error_string = NULL;
  512. if (response_len != MS_CHAP2_RESPONSE_LEN)
  513. break; /* not even the right length */
  514. ChallengeHash(&response[MS_CHAP2_PEER_CHALLENGE], challenge,
  515. user, Challenge);
  516. /* ship off to winbind, and check */
  517. if (run_ntlm_auth(username,
  518. domain,
  519. NULL,
  520. NULL,
  521. Challenge, 8,
  522. NULL, 0,
  523. &response[MS_CHAP2_NTRESP],
  524. MS_CHAP2_NTRESP_LEN,
  525. session_key,
  526. &error_string) == AUTHENTICATED) {
  527. GenerateAuthenticatorResponse(session_key,
  528. &response[MS_CHAP2_NTRESP],
  529. &response[MS_CHAP2_PEER_CHALLENGE],
  530. challenge, user, saresponse);
  531. mppe_set_keys2(session_key, &response[MS_CHAP2_NTRESP],
  532. MS_CHAP2_AUTHENTICATOR);
  533. if (response[MS_CHAP2_FLAGS]) {
  534. slprintf(message, message_space, "S=%s", saresponse);
  535. } else {
  536. slprintf(message, message_space, "S=%s M=%s",
  537. saresponse, "Access granted");
  538. }
  539. return AUTHENTICATED;
  540. } else {
  541. if (error_string) {
  542. notice(error_string);
  543. slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
  544. challenge_len, challenge, error_string);
  545. free(error_string);
  546. } else {
  547. slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
  548. challenge_len, challenge, "Access denied");
  549. }
  550. return NOT_AUTHENTICATED;
  551. }
  552. break;
  553. }
  554. default:
  555. error("WINBIND: Challenge type %u unsupported", digest->code);
  556. }
  557. return NOT_AUTHENTICATED;
  558. }
  559. static int
  560. winbind_allowed_address(u_int32_t addr)
  561. {
  562. ipcp_options *wo = &ipcp_wantoptions[0];
  563. if (wo->hisaddr !=0 && wo->hisaddr == addr) {
  564. return 1;
  565. }
  566. return -1;
  567. }