/crypto/heimdal/appl/ftp/ftpd/gssapi.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 511 lines · 414 code · 62 blank · 35 comment · 62 complexity · 31f453593cabf97ac83800a4c25d9f06 MD5 · raw file

  1. /*
  2. * Copyright (c) 1998 - 2005 Kungliga Tekniska Hรถgskolan
  3. * (Royal Institute of Technology, Stockholm, Sweden).
  4. * 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. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. *
  17. * 3. Neither the name of the Institute nor the names of its contributors
  18. * may be used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  22. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. */
  33. #ifdef FTP_SERVER
  34. #include "ftpd_locl.h"
  35. #else
  36. #include "ftp_locl.h"
  37. #endif
  38. #include <gssapi/gssapi.h>
  39. #include <gssapi/gssapi_krb5.h>
  40. #include <krb5_err.h>
  41. RCSID("$Id$");
  42. int ftp_do_gss_bindings = 0;
  43. int ftp_do_gss_delegate = 1;
  44. struct gssapi_data {
  45. gss_ctx_id_t context_hdl;
  46. gss_name_t client_name;
  47. gss_cred_id_t delegated_cred_handle;
  48. void *mech_data;
  49. };
  50. static int
  51. gss_init(void *app_data)
  52. {
  53. struct gssapi_data *d = app_data;
  54. d->context_hdl = GSS_C_NO_CONTEXT;
  55. d->delegated_cred_handle = GSS_C_NO_CREDENTIAL;
  56. #if defined(FTP_SERVER)
  57. return 0;
  58. #else
  59. /* XXX Check the gss mechanism; with gss_indicate_mechs() ? */
  60. #ifdef KRB5
  61. return !use_kerberos;
  62. #else
  63. return 0;
  64. #endif /* KRB5 */
  65. #endif /* FTP_SERVER */
  66. }
  67. static int
  68. gss_check_prot(void *app_data, int level)
  69. {
  70. if(level == prot_confidential)
  71. return -1;
  72. return 0;
  73. }
  74. static int
  75. gss_decode(void *app_data, void *buf, int len, int level)
  76. {
  77. OM_uint32 maj_stat, min_stat;
  78. gss_buffer_desc input, output;
  79. gss_qop_t qop_state;
  80. int conf_state;
  81. struct gssapi_data *d = app_data;
  82. size_t ret_len;
  83. input.length = len;
  84. input.value = buf;
  85. maj_stat = gss_unwrap (&min_stat,
  86. d->context_hdl,
  87. &input,
  88. &output,
  89. &conf_state,
  90. &qop_state);
  91. if(GSS_ERROR(maj_stat))
  92. return -1;
  93. memmove(buf, output.value, output.length);
  94. ret_len = output.length;
  95. gss_release_buffer(&min_stat, &output);
  96. return ret_len;
  97. }
  98. static int
  99. gss_overhead(void *app_data, int level, int len)
  100. {
  101. return 100; /* dunno? */
  102. }
  103. static int
  104. gss_encode(void *app_data, void *from, int length, int level, void **to)
  105. {
  106. OM_uint32 maj_stat, min_stat;
  107. gss_buffer_desc input, output;
  108. int conf_state;
  109. struct gssapi_data *d = app_data;
  110. input.length = length;
  111. input.value = from;
  112. maj_stat = gss_wrap (&min_stat,
  113. d->context_hdl,
  114. level == prot_private,
  115. GSS_C_QOP_DEFAULT,
  116. &input,
  117. &conf_state,
  118. &output);
  119. *to = output.value;
  120. return output.length;
  121. }
  122. static void
  123. sockaddr_to_gss_address (struct sockaddr *sa,
  124. OM_uint32 *addr_type,
  125. gss_buffer_desc *gss_addr)
  126. {
  127. switch (sa->sa_family) {
  128. #ifdef HAVE_IPV6
  129. case AF_INET6 : {
  130. struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
  131. gss_addr->length = 16;
  132. gss_addr->value = &sin6->sin6_addr;
  133. *addr_type = GSS_C_AF_INET6;
  134. break;
  135. }
  136. #endif
  137. case AF_INET : {
  138. struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
  139. gss_addr->length = 4;
  140. gss_addr->value = &sin4->sin_addr;
  141. *addr_type = GSS_C_AF_INET;
  142. break;
  143. }
  144. default :
  145. errx (1, "unknown address family %d", sa->sa_family);
  146. }
  147. }
  148. /* end common stuff */
  149. #ifdef FTP_SERVER
  150. static int
  151. gss_adat(void *app_data, void *buf, size_t len)
  152. {
  153. char *p = NULL;
  154. gss_buffer_desc input_token, output_token;
  155. OM_uint32 maj_stat, min_stat;
  156. gss_name_t client_name;
  157. struct gssapi_data *d = app_data;
  158. gss_channel_bindings_t bindings;
  159. if (ftp_do_gss_bindings) {
  160. bindings = malloc(sizeof(*bindings));
  161. if (bindings == NULL)
  162. errx(1, "out of memory");
  163. sockaddr_to_gss_address (his_addr,
  164. &bindings->initiator_addrtype,
  165. &bindings->initiator_address);
  166. sockaddr_to_gss_address (ctrl_addr,
  167. &bindings->acceptor_addrtype,
  168. &bindings->acceptor_address);
  169. bindings->application_data.length = 0;
  170. bindings->application_data.value = NULL;
  171. } else
  172. bindings = GSS_C_NO_CHANNEL_BINDINGS;
  173. input_token.value = buf;
  174. input_token.length = len;
  175. maj_stat = gss_accept_sec_context (&min_stat,
  176. &d->context_hdl,
  177. GSS_C_NO_CREDENTIAL,
  178. &input_token,
  179. bindings,
  180. &client_name,
  181. NULL,
  182. &output_token,
  183. NULL,
  184. NULL,
  185. &d->delegated_cred_handle);
  186. if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
  187. free(bindings);
  188. if(output_token.length) {
  189. if(base64_encode(output_token.value, output_token.length, &p) < 0) {
  190. reply(535, "Out of memory base64-encoding.");
  191. return -1;
  192. }
  193. gss_release_buffer(&min_stat, &output_token);
  194. }
  195. if(maj_stat == GSS_S_COMPLETE){
  196. d->client_name = client_name;
  197. client_name = GSS_C_NO_NAME;
  198. if(p)
  199. reply(235, "ADAT=%s", p);
  200. else
  201. reply(235, "ADAT Complete");
  202. sec_complete = 1;
  203. } else if(maj_stat == GSS_S_CONTINUE_NEEDED) {
  204. if(p)
  205. reply(335, "ADAT=%s", p);
  206. else
  207. reply(335, "OK, need more data");
  208. } else {
  209. OM_uint32 new_stat;
  210. OM_uint32 msg_ctx = 0;
  211. gss_buffer_desc status_string;
  212. gss_display_status(&new_stat,
  213. min_stat,
  214. GSS_C_MECH_CODE,
  215. GSS_C_NO_OID,
  216. &msg_ctx,
  217. &status_string);
  218. syslog(LOG_ERR, "gss_accept_sec_context: %.*s",
  219. (int)status_string.length,
  220. (char*)status_string.value);
  221. gss_release_buffer(&new_stat, &status_string);
  222. reply(431, "Security resource unavailable");
  223. }
  224. if (client_name)
  225. gss_release_name(&min_stat, &client_name);
  226. free(p);
  227. return 0;
  228. }
  229. int gssapi_userok(void*, char*);
  230. int gssapi_session(void*, char*);
  231. struct sec_server_mech gss_server_mech = {
  232. "GSSAPI",
  233. sizeof(struct gssapi_data),
  234. gss_init, /* init */
  235. NULL, /* end */
  236. gss_check_prot,
  237. gss_overhead,
  238. gss_encode,
  239. gss_decode,
  240. /* */
  241. NULL,
  242. gss_adat,
  243. NULL, /* pbsz */
  244. NULL, /* ccc */
  245. gssapi_userok,
  246. gssapi_session
  247. };
  248. #else /* FTP_SERVER */
  249. extern struct sockaddr *hisctladdr, *myctladdr;
  250. static int
  251. import_name(const char *kname, const char *host, gss_name_t *target_name)
  252. {
  253. OM_uint32 maj_stat, min_stat;
  254. gss_buffer_desc name;
  255. char *str;
  256. name.length = asprintf(&str, "%s@%s", kname, host);
  257. if (str == NULL) {
  258. printf("Out of memory\n");
  259. return AUTH_ERROR;
  260. }
  261. name.value = str;
  262. maj_stat = gss_import_name(&min_stat,
  263. &name,
  264. GSS_C_NT_HOSTBASED_SERVICE,
  265. target_name);
  266. if (GSS_ERROR(maj_stat)) {
  267. OM_uint32 new_stat;
  268. OM_uint32 msg_ctx = 0;
  269. gss_buffer_desc status_string;
  270. gss_display_status(&new_stat,
  271. min_stat,
  272. GSS_C_MECH_CODE,
  273. GSS_C_NO_OID,
  274. &msg_ctx,
  275. &status_string);
  276. printf("Error importing name %.*s: %.*s\n",
  277. (int)name.length,
  278. (char *)name.value,
  279. (int)status_string.length,
  280. (char *)status_string.value);
  281. free(name.value);
  282. gss_release_buffer(&new_stat, &status_string);
  283. return AUTH_ERROR;
  284. }
  285. free(name.value);
  286. return 0;
  287. }
  288. static int
  289. gss_auth(void *app_data, char *host)
  290. {
  291. OM_uint32 maj_stat, min_stat;
  292. gss_name_t target_name;
  293. gss_buffer_desc input, output_token;
  294. int context_established = 0;
  295. char *p;
  296. int n;
  297. gss_channel_bindings_t bindings;
  298. struct gssapi_data *d = app_data;
  299. OM_uint32 mech_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
  300. const char *knames[] = { "ftp", "host", NULL }, **kname = knames;
  301. if(import_name(*kname++, host, &target_name))
  302. return AUTH_ERROR;
  303. input.length = 0;
  304. input.value = NULL;
  305. if (ftp_do_gss_bindings) {
  306. bindings = malloc(sizeof(*bindings));
  307. if (bindings == NULL)
  308. errx(1, "out of memory");
  309. sockaddr_to_gss_address (myctladdr,
  310. &bindings->initiator_addrtype,
  311. &bindings->initiator_address);
  312. sockaddr_to_gss_address (hisctladdr,
  313. &bindings->acceptor_addrtype,
  314. &bindings->acceptor_address);
  315. bindings->application_data.length = 0;
  316. bindings->application_data.value = NULL;
  317. } else
  318. bindings = GSS_C_NO_CHANNEL_BINDINGS;
  319. if (ftp_do_gss_delegate)
  320. mech_flags |= GSS_C_DELEG_FLAG;
  321. while(!context_established) {
  322. maj_stat = gss_init_sec_context(&min_stat,
  323. GSS_C_NO_CREDENTIAL,
  324. &d->context_hdl,
  325. target_name,
  326. GSS_C_NO_OID,
  327. mech_flags,
  328. 0,
  329. bindings,
  330. &input,
  331. NULL,
  332. &output_token,
  333. NULL,
  334. NULL);
  335. if (GSS_ERROR(maj_stat)) {
  336. OM_uint32 new_stat;
  337. OM_uint32 msg_ctx = 0;
  338. gss_buffer_desc status_string;
  339. d->context_hdl = GSS_C_NO_CONTEXT;
  340. gss_release_name(&min_stat, &target_name);
  341. if(*kname != NULL) {
  342. if(import_name(*kname++, host, &target_name)) {
  343. if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
  344. free(bindings);
  345. return AUTH_ERROR;
  346. }
  347. continue;
  348. }
  349. if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
  350. free(bindings);
  351. gss_display_status(&new_stat,
  352. min_stat,
  353. GSS_C_MECH_CODE,
  354. GSS_C_NO_OID,
  355. &msg_ctx,
  356. &status_string);
  357. printf("Error initializing security context: %.*s\n",
  358. (int)status_string.length,
  359. (char*)status_string.value);
  360. gss_release_buffer(&new_stat, &status_string);
  361. return AUTH_CONTINUE;
  362. }
  363. if (input.value) {
  364. free(input.value);
  365. input.value = NULL;
  366. input.length = 0;
  367. }
  368. if (output_token.length != 0) {
  369. base64_encode(output_token.value, output_token.length, &p);
  370. gss_release_buffer(&min_stat, &output_token);
  371. n = command("ADAT %s", p);
  372. free(p);
  373. }
  374. if (GSS_ERROR(maj_stat)) {
  375. if (d->context_hdl != GSS_C_NO_CONTEXT)
  376. gss_delete_sec_context (&min_stat,
  377. &d->context_hdl,
  378. GSS_C_NO_BUFFER);
  379. break;
  380. }
  381. if (maj_stat & GSS_S_CONTINUE_NEEDED) {
  382. p = strstr(reply_string, "ADAT=");
  383. if(p == NULL){
  384. printf("Error: expected ADAT in reply. got: %s\n",
  385. reply_string);
  386. if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
  387. free(bindings);
  388. return AUTH_ERROR;
  389. } else {
  390. p+=5;
  391. input.value = malloc(strlen(p));
  392. input.length = base64_decode(p, input.value);
  393. }
  394. } else {
  395. if(code != 235) {
  396. printf("Unrecognized response code: %d\n", code);
  397. if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
  398. free(bindings);
  399. return AUTH_ERROR;
  400. }
  401. context_established = 1;
  402. }
  403. }
  404. gss_release_name(&min_stat, &target_name);
  405. if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
  406. free(bindings);
  407. if (input.value)
  408. free(input.value);
  409. {
  410. gss_name_t targ_name;
  411. maj_stat = gss_inquire_context(&min_stat,
  412. d->context_hdl,
  413. NULL,
  414. &targ_name,
  415. NULL,
  416. NULL,
  417. NULL,
  418. NULL,
  419. NULL);
  420. if (GSS_ERROR(maj_stat) == 0) {
  421. gss_buffer_desc name;
  422. maj_stat = gss_display_name (&min_stat,
  423. targ_name,
  424. &name,
  425. NULL);
  426. if (GSS_ERROR(maj_stat) == 0) {
  427. printf("Authenticated to <%.*s>\n",
  428. (int)name.length,
  429. (char *)name.value);
  430. gss_release_buffer(&min_stat, &name);
  431. }
  432. gss_release_name(&min_stat, &targ_name);
  433. } else
  434. printf("Failed to get gss name of peer.\n");
  435. }
  436. return AUTH_OK;
  437. }
  438. struct sec_client_mech gss_client_mech = {
  439. "GSSAPI",
  440. sizeof(struct gssapi_data),
  441. gss_init,
  442. gss_auth,
  443. NULL, /* end */
  444. gss_check_prot,
  445. gss_overhead,
  446. gss_encode,
  447. gss_decode,
  448. };
  449. #endif /* FTP_SERVER */