ssh-pkcs11-uri.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /*
  2. * Copyright (c) 2017 Red Hat
  3. *
  4. * Authors: Jakub Jelen <jjelen@redhat.com>
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #include "includes.h"
  19. #ifdef ENABLE_PKCS11
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include "sshkey.h"
  23. #include "sshbuf.h"
  24. #include "log.h"
  25. #define CRYPTOKI_COMPAT
  26. #include "pkcs11.h"
  27. #include "ssh-pkcs11-uri.h"
  28. #define PKCS11_URI_PATH_SEPARATOR ";"
  29. #define PKCS11_URI_QUERY_SEPARATOR "&"
  30. #define PKCS11_URI_VALUE_SEPARATOR "="
  31. #define PKCS11_URI_ID "id"
  32. #define PKCS11_URI_TOKEN "token"
  33. #define PKCS11_URI_OBJECT "object"
  34. #define PKCS11_URI_LIB_MANUF "library-manufacturer"
  35. #define PKCS11_URI_MANUF "manufacturer"
  36. #define PKCS11_URI_MODULE_PATH "module-path"
  37. #define PKCS11_URI_PIN_VALUE "pin-value"
  38. /* Keyword tokens. */
  39. typedef enum {
  40. pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath,
  41. pPinValue, pBadOption
  42. } pkcs11uriOpCodes;
  43. /* Textual representation of the tokens. */
  44. static struct {
  45. const char *name;
  46. pkcs11uriOpCodes opcode;
  47. } keywords[] = {
  48. { PKCS11_URI_ID, pId },
  49. { PKCS11_URI_TOKEN, pToken },
  50. { PKCS11_URI_OBJECT, pObject },
  51. { PKCS11_URI_LIB_MANUF, pLibraryManufacturer },
  52. { PKCS11_URI_MANUF, pManufacturer },
  53. { PKCS11_URI_MODULE_PATH, pModulePath },
  54. { PKCS11_URI_PIN_VALUE, pPinValue },
  55. { NULL, pBadOption }
  56. };
  57. static pkcs11uriOpCodes
  58. parse_token(const char *cp)
  59. {
  60. u_int i;
  61. for (i = 0; keywords[i].name; i++)
  62. if (strncasecmp(cp, keywords[i].name,
  63. strlen(keywords[i].name)) == 0)
  64. return keywords[i].opcode;
  65. return pBadOption;
  66. }
  67. int
  68. percent_decode(char *data, char **outp)
  69. {
  70. char tmp[3];
  71. char *out, *tmp_end;
  72. char *p = data;
  73. long value;
  74. size_t outlen = 0;
  75. out = malloc(strlen(data)+1); /* upper bound */
  76. if (out == NULL)
  77. return -1;
  78. while (*p != '\0') {
  79. switch (*p) {
  80. case '%':
  81. p++;
  82. if (*p == '\0')
  83. goto fail;
  84. tmp[0] = *p++;
  85. if (*p == '\0')
  86. goto fail;
  87. tmp[1] = *p++;
  88. tmp[2] = '\0';
  89. tmp_end = NULL;
  90. value = strtol(tmp, &tmp_end, 16);
  91. if (tmp_end != tmp+2)
  92. goto fail;
  93. else
  94. out[outlen++] = (char) value;
  95. break;
  96. default:
  97. out[outlen++] = *p++;
  98. break;
  99. }
  100. }
  101. /* zero terminate */
  102. out[outlen] = '\0';
  103. *outp = out;
  104. return outlen;
  105. fail:
  106. free(out);
  107. return -1;
  108. }
  109. struct sshbuf *
  110. percent_encode(const char *data, size_t length, const char *allow_list)
  111. {
  112. struct sshbuf *b = NULL;
  113. char tmp[4], *cp;
  114. size_t i;
  115. if ((b = sshbuf_new()) == NULL)
  116. return NULL;
  117. for (i = 0; i < length; i++) {
  118. cp = strchr(allow_list, data[i]);
  119. /* if c is specified as '\0' pointer to terminator is returned !! */
  120. if (cp != NULL && *cp != '\0') {
  121. if (sshbuf_put(b, &data[i], 1) != 0)
  122. goto err;
  123. } else
  124. if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3
  125. || sshbuf_put(b, tmp, 3) != 0)
  126. goto err;
  127. }
  128. if (sshbuf_put(b, "\0", 1) == 0)
  129. return b;
  130. err:
  131. sshbuf_free(b);
  132. return NULL;
  133. }
  134. char *
  135. pkcs11_uri_append(char *part, const char *separator, const char *key,
  136. struct sshbuf *value)
  137. {
  138. char *new_part;
  139. size_t size = 0;
  140. if (value == NULL)
  141. return NULL;
  142. size = asprintf(&new_part,
  143. "%s%s%s" PKCS11_URI_VALUE_SEPARATOR "%s",
  144. (part != NULL ? part : ""),
  145. (part != NULL ? separator : ""),
  146. key, sshbuf_ptr(value));
  147. sshbuf_free(value);
  148. free(part);
  149. if (size <= 0)
  150. return NULL;
  151. return new_part;
  152. }
  153. char *
  154. pkcs11_uri_get(struct pkcs11_uri *uri)
  155. {
  156. size_t size = 0;
  157. char *p = NULL, *path = NULL, *query = NULL;
  158. /* compose a percent-encoded ID */
  159. if (uri->id_len > 0) {
  160. struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, "");
  161. path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
  162. PKCS11_URI_ID, key_id);
  163. if (path == NULL)
  164. goto err;
  165. }
  166. /* Write object label */
  167. if (uri->object) {
  168. struct sshbuf *label = percent_encode(uri->object, strlen(uri->object),
  169. PKCS11_URI_WHITELIST);
  170. path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
  171. PKCS11_URI_OBJECT, label);
  172. if (path == NULL)
  173. goto err;
  174. }
  175. /* Write token label */
  176. if (uri->token) {
  177. struct sshbuf *label = percent_encode(uri->token, strlen(uri->token),
  178. PKCS11_URI_WHITELIST);
  179. path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
  180. PKCS11_URI_TOKEN, label);
  181. if (path == NULL)
  182. goto err;
  183. }
  184. /* Write manufacturer */
  185. if (uri->manuf) {
  186. struct sshbuf *manuf = percent_encode(uri->manuf,
  187. strlen(uri->manuf), PKCS11_URI_WHITELIST);
  188. path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR,
  189. PKCS11_URI_MANUF, manuf);
  190. if (path == NULL)
  191. goto err;
  192. }
  193. /* Write module_path */
  194. if (uri->module_path) {
  195. struct sshbuf *module = percent_encode(uri->module_path,
  196. strlen(uri->module_path), PKCS11_URI_WHITELIST "/");
  197. query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR,
  198. PKCS11_URI_MODULE_PATH, module);
  199. if (query == NULL)
  200. goto err;
  201. }
  202. size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s",
  203. path != NULL ? path : "",
  204. query != NULL ? "?" : "",
  205. query != NULL ? query : "");
  206. err:
  207. free(query);
  208. free(path);
  209. if (size <= 0)
  210. return NULL;
  211. return p;
  212. }
  213. struct pkcs11_uri *
  214. pkcs11_uri_init()
  215. {
  216. struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri));
  217. return d;
  218. }
  219. void
  220. pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11)
  221. {
  222. if (pkcs11 == NULL) {
  223. return;
  224. }
  225. free(pkcs11->id);
  226. free(pkcs11->module_path);
  227. free(pkcs11->token);
  228. free(pkcs11->object);
  229. free(pkcs11->lib_manuf);
  230. free(pkcs11->manuf);
  231. if (pkcs11->pin)
  232. freezero(pkcs11->pin, strlen(pkcs11->pin));
  233. free(pkcs11);
  234. }
  235. int
  236. pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11)
  237. {
  238. char *saveptr1, *saveptr2, *str1, *str2, *tok;
  239. int rv = 0, len;
  240. char *p = NULL;
  241. size_t scheme_len = strlen(PKCS11_URI_SCHEME);
  242. if (strlen(uri) < scheme_len || /* empty URI matches everything */
  243. strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) {
  244. error("%s: The '%s' does not look like PKCS#11 URI",
  245. __func__, uri);
  246. return -1;
  247. }
  248. if (pkcs11 == NULL) {
  249. error("%s: Bad arguments. The pkcs11 can't be null", __func__);
  250. return -1;
  251. }
  252. /* skip URI schema name */
  253. p = strdup(uri);
  254. str1 = p;
  255. /* everything before ? */
  256. tok = strtok_r(str1, "?", &saveptr1);
  257. if (tok == NULL) {
  258. error("%s: pk11-path expected, got EOF", __func__);
  259. rv = -1;
  260. goto out;
  261. }
  262. /* skip URI schema name:
  263. * the scheme ensures that there is at least something before "?"
  264. * allowing empty pk11-path. Resulting token at worst pointing to
  265. * \0 byte */
  266. tok = tok + scheme_len;
  267. /* parse pk11-path */
  268. for (str2 = tok; ; str2 = NULL) {
  269. char **charptr, *arg = NULL;
  270. pkcs11uriOpCodes opcode;
  271. tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2);
  272. if (tok == NULL)
  273. break;
  274. opcode = parse_token(tok);
  275. if (opcode != pBadOption)
  276. arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
  277. switch (opcode) {
  278. case pId:
  279. /* CKA_ID */
  280. if (pkcs11->id != NULL) {
  281. verbose("%s: The id already set in the PKCS#11 URI",
  282. __func__);
  283. rv = -1;
  284. goto out;
  285. }
  286. len = percent_decode(arg, &pkcs11->id);
  287. if (len <= 0) {
  288. verbose("%s: Failed to percent-decode CKA_ID: %s",
  289. __func__, arg);
  290. rv = -1;
  291. goto out;
  292. } else
  293. pkcs11->id_len = len;
  294. debug3("%s: Setting CKA_ID = %s from PKCS#11 URI",
  295. __func__, arg);
  296. break;
  297. case pToken:
  298. /* CK_TOKEN_INFO -> label */
  299. charptr = &pkcs11->token;
  300. parse_string:
  301. if (*charptr != NULL) {
  302. verbose("%s: The %s already set in the PKCS#11 URI",
  303. keywords[opcode].name, __func__);
  304. rv = -1;
  305. goto out;
  306. }
  307. percent_decode(arg, charptr);
  308. debug3("%s: Setting %s = %s from PKCS#11 URI",
  309. __func__, keywords[opcode].name, *charptr);
  310. break;
  311. case pObject:
  312. /* CK_TOKEN_INFO -> manufacturerID */
  313. charptr = &pkcs11->object;
  314. goto parse_string;
  315. case pManufacturer:
  316. /* CK_TOKEN_INFO -> manufacturerID */
  317. charptr = &pkcs11->manuf;
  318. goto parse_string;
  319. case pLibraryManufacturer:
  320. /* CK_INFO -> manufacturerID */
  321. charptr = &pkcs11->lib_manuf;
  322. goto parse_string;
  323. default:
  324. /* Unrecognized attribute in the URI path SHOULD be error */
  325. verbose("%s: Unknown part of path in PKCS#11 URI: %s",
  326. __func__, tok);
  327. }
  328. }
  329. tok = strtok_r(NULL, "?", &saveptr1);
  330. if (tok == NULL) {
  331. goto out;
  332. }
  333. /* parse pk11-query (optional) */
  334. for (str2 = tok; ; str2 = NULL) {
  335. char *arg;
  336. pkcs11uriOpCodes opcode;
  337. tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2);
  338. if (tok == NULL)
  339. break;
  340. opcode = parse_token(tok);
  341. if (opcode != pBadOption)
  342. arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
  343. switch (opcode) {
  344. case pModulePath:
  345. /* module-path is PKCS11Provider */
  346. if (pkcs11->module_path != NULL) {
  347. verbose("%s: Multiple module-path attributes are"
  348. "not supported the PKCS#11 URI", __func__);
  349. rv = -1;
  350. goto out;
  351. }
  352. percent_decode(arg, &pkcs11->module_path);
  353. debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI",
  354. __func__, pkcs11->module_path);
  355. break;
  356. case pPinValue:
  357. /* pin-value */
  358. if (pkcs11->pin != NULL) {
  359. verbose("%s: Multiple pin-value attributes are"
  360. "not supported the PKCS#11 URI", __func__);
  361. rv = -1;
  362. goto out;
  363. }
  364. percent_decode(arg, &pkcs11->pin);
  365. debug3("%s: Setting PIN from PKCS#11 URI", __func__);
  366. break;
  367. default:
  368. /* Unrecognized attribute in the URI query SHOULD be ignored */
  369. verbose("%s: Unknown part of query in PKCS#11 URI: %s",
  370. __func__, tok);
  371. }
  372. }
  373. out:
  374. free(p);
  375. return rv;
  376. }
  377. #endif /* ENABLE_PKCS11 */