cifs_spnego.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // SPDX-License-Identifier: LGPL-2.1
  2. /*
  3. * SPNEGO upcall management for CIFS
  4. *
  5. * Copyright (c) 2007 Red Hat, Inc.
  6. * Author(s): Jeff Layton ([email protected])
  7. *
  8. */
  9. #include <linux/list.h>
  10. #include <linux/slab.h>
  11. #include <linux/string.h>
  12. #include <keys/user-type.h>
  13. #include <linux/key-type.h>
  14. #include <linux/keyctl.h>
  15. #include <linux/inet.h>
  16. #include "cifsglob.h"
  17. #include "cifs_spnego.h"
  18. #include "cifs_debug.h"
  19. #include "cifsproto.h"
  20. static const struct cred *spnego_cred;
  21. /* create a new cifs key */
  22. static int
  23. cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
  24. {
  25. char *payload;
  26. int ret;
  27. ret = -ENOMEM;
  28. payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL);
  29. if (!payload)
  30. goto error;
  31. /* attach the data */
  32. key->payload.data[0] = payload;
  33. ret = 0;
  34. error:
  35. return ret;
  36. }
  37. static void
  38. cifs_spnego_key_destroy(struct key *key)
  39. {
  40. kfree(key->payload.data[0]);
  41. }
  42. /*
  43. * keytype for CIFS spnego keys
  44. */
  45. struct key_type cifs_spnego_key_type = {
  46. .name = "cifs.spnego",
  47. .instantiate = cifs_spnego_key_instantiate,
  48. .destroy = cifs_spnego_key_destroy,
  49. .describe = user_describe,
  50. };
  51. /* length of longest version string e.g. strlen("ver=0xFF") */
  52. #define MAX_VER_STR_LEN 8
  53. /* length of longest security mechanism name, eg in future could have
  54. * strlen(";sec=ntlmsspi") */
  55. #define MAX_MECH_STR_LEN 13
  56. /* strlen of ";host=" */
  57. #define HOST_KEY_LEN 6
  58. /* strlen of ";ip4=" or ";ip6=" */
  59. #define IP_KEY_LEN 5
  60. /* strlen of ";uid=0x" */
  61. #define UID_KEY_LEN 7
  62. /* strlen of ";creduid=0x" */
  63. #define CREDUID_KEY_LEN 11
  64. /* strlen of ";user=" */
  65. #define USER_KEY_LEN 6
  66. /* strlen of ";pid=0x" */
  67. #define PID_KEY_LEN 7
  68. /* get a key struct with a SPNEGO security blob, suitable for session setup */
  69. struct key *
  70. cifs_get_spnego_key(struct cifs_ses *sesInfo,
  71. struct TCP_Server_Info *server)
  72. {
  73. struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
  74. struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
  75. char *description, *dp;
  76. size_t desc_len;
  77. struct key *spnego_key;
  78. const char *hostname = server->hostname;
  79. const struct cred *saved_cred;
  80. /* length of fields (with semicolons): ver=0xyz ip4=ipaddress
  81. host=hostname sec=mechanism uid=0xFF user=username */
  82. desc_len = MAX_VER_STR_LEN +
  83. HOST_KEY_LEN + strlen(hostname) +
  84. IP_KEY_LEN + INET6_ADDRSTRLEN +
  85. MAX_MECH_STR_LEN +
  86. UID_KEY_LEN + (sizeof(uid_t) * 2) +
  87. CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
  88. PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
  89. if (sesInfo->user_name)
  90. desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
  91. spnego_key = ERR_PTR(-ENOMEM);
  92. description = kzalloc(desc_len, GFP_KERNEL);
  93. if (description == NULL)
  94. goto out;
  95. dp = description;
  96. /* start with version and hostname portion of UNC string */
  97. spnego_key = ERR_PTR(-EINVAL);
  98. sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
  99. hostname);
  100. dp = description + strlen(description);
  101. /* add the server address */
  102. if (server->dstaddr.ss_family == AF_INET)
  103. sprintf(dp, "ip4=%pI4", &sa->sin_addr);
  104. else if (server->dstaddr.ss_family == AF_INET6)
  105. sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
  106. else
  107. goto out;
  108. dp = description + strlen(description);
  109. /* for now, only sec=krb5 and sec=mskrb5 are valid */
  110. if (server->sec_kerberos)
  111. sprintf(dp, ";sec=krb5");
  112. else if (server->sec_mskerberos)
  113. sprintf(dp, ";sec=mskrb5");
  114. else {
  115. cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
  116. sprintf(dp, ";sec=krb5");
  117. }
  118. dp = description + strlen(description);
  119. sprintf(dp, ";uid=0x%x",
  120. from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
  121. dp = description + strlen(description);
  122. sprintf(dp, ";creduid=0x%x",
  123. from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
  124. if (sesInfo->user_name) {
  125. dp = description + strlen(description);
  126. sprintf(dp, ";user=%s", sesInfo->user_name);
  127. }
  128. dp = description + strlen(description);
  129. sprintf(dp, ";pid=0x%x", current->pid);
  130. cifs_dbg(FYI, "key description = %s\n", description);
  131. saved_cred = override_creds(spnego_cred);
  132. spnego_key = request_key(&cifs_spnego_key_type, description, "");
  133. revert_creds(saved_cred);
  134. #ifdef CONFIG_CIFS_DEBUG2
  135. if (cifsFYI && !IS_ERR(spnego_key)) {
  136. struct cifs_spnego_msg *msg = spnego_key->payload.data[0];
  137. cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U,
  138. msg->secblob_len + msg->sesskey_len));
  139. }
  140. #endif /* CONFIG_CIFS_DEBUG2 */
  141. out:
  142. kfree(description);
  143. return spnego_key;
  144. }
  145. int
  146. init_cifs_spnego(void)
  147. {
  148. struct cred *cred;
  149. struct key *keyring;
  150. int ret;
  151. cifs_dbg(FYI, "Registering the %s key type\n",
  152. cifs_spnego_key_type.name);
  153. /*
  154. * Create an override credential set with special thread keyring for
  155. * spnego upcalls.
  156. */
  157. cred = prepare_kernel_cred(NULL);
  158. if (!cred)
  159. return -ENOMEM;
  160. keyring = keyring_alloc(".cifs_spnego",
  161. GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
  162. (KEY_POS_ALL & ~KEY_POS_SETATTR) |
  163. KEY_USR_VIEW | KEY_USR_READ,
  164. KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
  165. if (IS_ERR(keyring)) {
  166. ret = PTR_ERR(keyring);
  167. goto failed_put_cred;
  168. }
  169. ret = register_key_type(&cifs_spnego_key_type);
  170. if (ret < 0)
  171. goto failed_put_key;
  172. /*
  173. * instruct request_key() to use this special keyring as a cache for
  174. * the results it looks up
  175. */
  176. set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
  177. cred->thread_keyring = keyring;
  178. cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
  179. spnego_cred = cred;
  180. cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
  181. return 0;
  182. failed_put_key:
  183. key_put(keyring);
  184. failed_put_cred:
  185. put_cred(cred);
  186. return ret;
  187. }
  188. void
  189. exit_cifs_spnego(void)
  190. {
  191. key_revoke(spnego_cred->thread_keyring);
  192. unregister_key_type(&cifs_spnego_key_type);
  193. put_cred(spnego_cred);
  194. cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
  195. }