476 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * algif_hash: User-space interface for hash algorithms4 *5 * This file provides the user-space API for hash algorithms.6 *7 * Copyright (c) 2010 Herbert Xu <herbert@gondor.apana.org.au>8 */9 10#include <crypto/hash.h>11#include <crypto/if_alg.h>12#include <linux/init.h>13#include <linux/kernel.h>14#include <linux/mm.h>15#include <linux/module.h>16#include <linux/net.h>17#include <net/sock.h>18 19struct hash_ctx {20 struct af_alg_sgl sgl;21 22 u8 *result;23 24 struct crypto_wait wait;25 26 unsigned int len;27 bool more;28 29 struct ahash_request req;30};31 32static int hash_alloc_result(struct sock *sk, struct hash_ctx *ctx)33{34 unsigned ds;35 36 if (ctx->result)37 return 0;38 39 ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));40 41 ctx->result = sock_kmalloc(sk, ds, GFP_KERNEL);42 if (!ctx->result)43 return -ENOMEM;44 45 memset(ctx->result, 0, ds);46 47 return 0;48}49 50static void hash_free_result(struct sock *sk, struct hash_ctx *ctx)51{52 unsigned ds;53 54 if (!ctx->result)55 return;56 57 ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));58 59 sock_kzfree_s(sk, ctx->result, ds);60 ctx->result = NULL;61}62 63static int hash_sendmsg(struct socket *sock, struct msghdr *msg,64 size_t ignored)65{66 struct sock *sk = sock->sk;67 struct alg_sock *ask = alg_sk(sk);68 struct hash_ctx *ctx = ask->private;69 ssize_t copied = 0;70 size_t len, max_pages, npages;71 bool continuing, need_init = false;72 int err;73 74 max_pages = min_t(size_t, ALG_MAX_PAGES,75 DIV_ROUND_UP(sk->sk_sndbuf, PAGE_SIZE));76 77 lock_sock(sk);78 continuing = ctx->more;79 80 if (!continuing) {81 /* Discard a previous request that wasn't marked MSG_MORE. */82 hash_free_result(sk, ctx);83 if (!msg_data_left(msg))84 goto done; /* Zero-length; don't start new req */85 need_init = true;86 } else if (!msg_data_left(msg)) {87 /*88 * No data - finalise the prev req if MSG_MORE so any error89 * comes out here.90 */91 if (!(msg->msg_flags & MSG_MORE)) {92 err = hash_alloc_result(sk, ctx);93 if (err)94 goto unlock_free_result;95 ahash_request_set_crypt(&ctx->req, NULL,96 ctx->result, 0);97 err = crypto_wait_req(crypto_ahash_final(&ctx->req),98 &ctx->wait);99 if (err)100 goto unlock_free_result;101 }102 goto done_more;103 }104 105 while (msg_data_left(msg)) {106 ctx->sgl.sgt.sgl = ctx->sgl.sgl;107 ctx->sgl.sgt.nents = 0;108 ctx->sgl.sgt.orig_nents = 0;109 110 err = -EIO;111 npages = iov_iter_npages(&msg->msg_iter, max_pages);112 if (npages == 0)113 goto unlock_free;114 115 sg_init_table(ctx->sgl.sgl, npages);116 117 ctx->sgl.need_unpin = iov_iter_extract_will_pin(&msg->msg_iter);118 119 err = extract_iter_to_sg(&msg->msg_iter, LONG_MAX,120 &ctx->sgl.sgt, npages, 0);121 if (err < 0)122 goto unlock_free;123 len = err;124 sg_mark_end(ctx->sgl.sgt.sgl + ctx->sgl.sgt.nents - 1);125 126 if (!msg_data_left(msg)) {127 err = hash_alloc_result(sk, ctx);128 if (err)129 goto unlock_free;130 }131 132 ahash_request_set_crypt(&ctx->req, ctx->sgl.sgt.sgl,133 ctx->result, len);134 135 if (!msg_data_left(msg) && !continuing &&136 !(msg->msg_flags & MSG_MORE)) {137 err = crypto_ahash_digest(&ctx->req);138 } else {139 if (need_init) {140 err = crypto_wait_req(141 crypto_ahash_init(&ctx->req),142 &ctx->wait);143 if (err)144 goto unlock_free;145 need_init = false;146 }147 148 if (msg_data_left(msg) || (msg->msg_flags & MSG_MORE))149 err = crypto_ahash_update(&ctx->req);150 else151 err = crypto_ahash_finup(&ctx->req);152 continuing = true;153 }154 155 err = crypto_wait_req(err, &ctx->wait);156 if (err)157 goto unlock_free;158 159 copied += len;160 af_alg_free_sg(&ctx->sgl);161 }162 163done_more:164 ctx->more = msg->msg_flags & MSG_MORE;165done:166 err = 0;167unlock:168 release_sock(sk);169 return copied ?: err;170 171unlock_free:172 af_alg_free_sg(&ctx->sgl);173unlock_free_result:174 hash_free_result(sk, ctx);175 ctx->more = false;176 goto unlock;177}178 179static int hash_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,180 int flags)181{182 struct sock *sk = sock->sk;183 struct alg_sock *ask = alg_sk(sk);184 struct hash_ctx *ctx = ask->private;185 unsigned ds = crypto_ahash_digestsize(crypto_ahash_reqtfm(&ctx->req));186 bool result;187 int err;188 189 if (len > ds)190 len = ds;191 else if (len < ds)192 msg->msg_flags |= MSG_TRUNC;193 194 lock_sock(sk);195 result = ctx->result;196 err = hash_alloc_result(sk, ctx);197 if (err)198 goto unlock;199 200 ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0);201 202 if (!result && !ctx->more) {203 err = crypto_wait_req(crypto_ahash_init(&ctx->req),204 &ctx->wait);205 if (err)206 goto unlock;207 }208 209 if (!result || ctx->more) {210 ctx->more = false;211 err = crypto_wait_req(crypto_ahash_final(&ctx->req),212 &ctx->wait);213 if (err)214 goto unlock;215 }216 217 err = memcpy_to_msg(msg, ctx->result, len);218 219unlock:220 hash_free_result(sk, ctx);221 release_sock(sk);222 223 return err ?: len;224}225 226static int hash_accept(struct socket *sock, struct socket *newsock,227 struct proto_accept_arg *arg)228{229 struct sock *sk = sock->sk;230 struct alg_sock *ask = alg_sk(sk);231 struct hash_ctx *ctx = ask->private;232 struct ahash_request *req = &ctx->req;233 struct crypto_ahash *tfm;234 struct sock *sk2;235 struct alg_sock *ask2;236 struct hash_ctx *ctx2;237 char *state;238 bool more;239 int err;240 241 tfm = crypto_ahash_reqtfm(req);242 state = kmalloc(crypto_ahash_statesize(tfm), GFP_KERNEL);243 err = -ENOMEM;244 if (!state)245 goto out;246 247 lock_sock(sk);248 more = ctx->more;249 err = more ? crypto_ahash_export(req, state) : 0;250 release_sock(sk);251 252 if (err)253 goto out_free_state;254 255 err = af_alg_accept(ask->parent, newsock, arg);256 if (err)257 goto out_free_state;258 259 sk2 = newsock->sk;260 ask2 = alg_sk(sk2);261 ctx2 = ask2->private;262 ctx2->more = more;263 264 if (!more)265 goto out_free_state;266 267 err = crypto_ahash_import(&ctx2->req, state);268 if (err) {269 sock_orphan(sk2);270 sock_put(sk2);271 }272 273out_free_state:274 kfree_sensitive(state);275 276out:277 return err;278}279 280static struct proto_ops algif_hash_ops = {281 .family = PF_ALG,282 283 .connect = sock_no_connect,284 .socketpair = sock_no_socketpair,285 .getname = sock_no_getname,286 .ioctl = sock_no_ioctl,287 .listen = sock_no_listen,288 .shutdown = sock_no_shutdown,289 .mmap = sock_no_mmap,290 .bind = sock_no_bind,291 292 .release = af_alg_release,293 .sendmsg = hash_sendmsg,294 .recvmsg = hash_recvmsg,295 .accept = hash_accept,296};297 298static int hash_check_key(struct socket *sock)299{300 int err = 0;301 struct sock *psk;302 struct alg_sock *pask;303 struct crypto_ahash *tfm;304 struct sock *sk = sock->sk;305 struct alg_sock *ask = alg_sk(sk);306 307 lock_sock(sk);308 if (!atomic_read(&ask->nokey_refcnt))309 goto unlock_child;310 311 psk = ask->parent;312 pask = alg_sk(ask->parent);313 tfm = pask->private;314 315 err = -ENOKEY;316 lock_sock_nested(psk, SINGLE_DEPTH_NESTING);317 if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)318 goto unlock;319 320 atomic_dec(&pask->nokey_refcnt);321 atomic_set(&ask->nokey_refcnt, 0);322 323 err = 0;324 325unlock:326 release_sock(psk);327unlock_child:328 release_sock(sk);329 330 return err;331}332 333static int hash_sendmsg_nokey(struct socket *sock, struct msghdr *msg,334 size_t size)335{336 int err;337 338 err = hash_check_key(sock);339 if (err)340 return err;341 342 return hash_sendmsg(sock, msg, size);343}344 345static int hash_recvmsg_nokey(struct socket *sock, struct msghdr *msg,346 size_t ignored, int flags)347{348 int err;349 350 err = hash_check_key(sock);351 if (err)352 return err;353 354 return hash_recvmsg(sock, msg, ignored, flags);355}356 357static int hash_accept_nokey(struct socket *sock, struct socket *newsock,358 struct proto_accept_arg *arg)359{360 int err;361 362 err = hash_check_key(sock);363 if (err)364 return err;365 366 return hash_accept(sock, newsock, arg);367}368 369static struct proto_ops algif_hash_ops_nokey = {370 .family = PF_ALG,371 372 .connect = sock_no_connect,373 .socketpair = sock_no_socketpair,374 .getname = sock_no_getname,375 .ioctl = sock_no_ioctl,376 .listen = sock_no_listen,377 .shutdown = sock_no_shutdown,378 .mmap = sock_no_mmap,379 .bind = sock_no_bind,380 381 .release = af_alg_release,382 .sendmsg = hash_sendmsg_nokey,383 .recvmsg = hash_recvmsg_nokey,384 .accept = hash_accept_nokey,385};386 387static void *hash_bind(const char *name, u32 type, u32 mask)388{389 return crypto_alloc_ahash(name, type, mask);390}391 392static void hash_release(void *private)393{394 crypto_free_ahash(private);395}396 397static int hash_setkey(void *private, const u8 *key, unsigned int keylen)398{399 return crypto_ahash_setkey(private, key, keylen);400}401 402static void hash_sock_destruct(struct sock *sk)403{404 struct alg_sock *ask = alg_sk(sk);405 struct hash_ctx *ctx = ask->private;406 407 hash_free_result(sk, ctx);408 sock_kfree_s(sk, ctx, ctx->len);409 af_alg_release_parent(sk);410}411 412static int hash_accept_parent_nokey(void *private, struct sock *sk)413{414 struct crypto_ahash *tfm = private;415 struct alg_sock *ask = alg_sk(sk);416 struct hash_ctx *ctx;417 unsigned int len = sizeof(*ctx) + crypto_ahash_reqsize(tfm);418 419 ctx = sock_kmalloc(sk, len, GFP_KERNEL);420 if (!ctx)421 return -ENOMEM;422 423 ctx->result = NULL;424 ctx->len = len;425 ctx->more = false;426 crypto_init_wait(&ctx->wait);427 428 ask->private = ctx;429 430 ahash_request_set_tfm(&ctx->req, tfm);431 ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG,432 crypto_req_done, &ctx->wait);433 434 sk->sk_destruct = hash_sock_destruct;435 436 return 0;437}438 439static int hash_accept_parent(void *private, struct sock *sk)440{441 struct crypto_ahash *tfm = private;442 443 if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)444 return -ENOKEY;445 446 return hash_accept_parent_nokey(private, sk);447}448 449static const struct af_alg_type algif_type_hash = {450 .bind = hash_bind,451 .release = hash_release,452 .setkey = hash_setkey,453 .accept = hash_accept_parent,454 .accept_nokey = hash_accept_parent_nokey,455 .ops = &algif_hash_ops,456 .ops_nokey = &algif_hash_ops_nokey,457 .name = "hash",458 .owner = THIS_MODULE459};460 461static int __init algif_hash_init(void)462{463 return af_alg_register_type(&algif_type_hash);464}465 466static void __exit algif_hash_exit(void)467{468 int err = af_alg_unregister_type(&algif_type_hash);469 BUG_ON(err);470}471 472module_init(algif_hash_init);473module_exit(algif_hash_exit);474MODULE_DESCRIPTION("Userspace interface for hash algorithms");475MODULE_LICENSE("GPL");476