1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
/* BLURB lgpl
Coda File System
Release 6
Copyright (c) 2005-2006 Carnegie Mellon University
Additional copyrights listed below
This code is distributed "AS IS" without warranty of any kind under
the terms of the GNU Library General Public Licence Version 2, as
shown in the file LICENSE. The technical and financial contributors to
Coda are listed in the file CREDITS.
Additional copyrights
#*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <rpc2/secure.h>
#include "grunt.h"
ssize_t secure_sendto(int s, const void *buf, size_t len, int flags,
const struct sockaddr *to, socklen_t tolen,
struct security_association *sa)
{
uint8_t out[MAXPACKETSIZE];
size_t padded_size;
ssize_t n;
int i, pad_align, padding;
uint8_t *aad, *iv, *payload, *icv;
if (!sa || (!sa->encrypt && !sa->authenticate)) {
/* make sure the other side will not mistake this as encrypted */
if (len >= 2 * sizeof(uint32_t) &&
ntohl(*int32(buf)) >= 256)
{
errno = EINVAL;
return -1;
}
n = len;
goto send_packet;
}
/* check for sequence number overflow */
sa->peer_seq++;
if (sa->peer_seq == 0) {
/* log(sa, "exhausted sequence number space"); */
sa->peer_seq--;
errno = EPIPE;
return -1;
}
/* calculate the amount of padding we will need */
pad_align = (sa->encrypt->blocksize > sizeof(uint32_t) ?
sa->encrypt->blocksize : sizeof(uint32_t)) - 1;
padded_size = (len + 2 * sizeof(uint8_t) + pad_align) & ~pad_align;
assert(padded_size - len >= 2 * sizeof(uint8_t));
padding = padded_size - 2 * sizeof(uint8_t) - len;
/* check if there is enough room */
if ((2 * sizeof(uint32_t) + sa->encrypt->iv_len + padded_size + sa->authenticate->icv_len) > sizeof(out))
{
errno = EMSGSIZE;
return -1;
}
/* pack the header */
aad = out;
int32(aad)[0] = htonl(sa->peer_spi);
int32(aad)[1] = htonl(sa->peer_seq);
iv = aad + 2 * sizeof(uint32_t);
/* increment and copy iv */
if (sa->encrypt->iv_len) {
for (i = sa->encrypt->iv_len - 1; i >= 0; i--)
if (++(sa->send_iv[i]))
break;
memcpy(iv, sa->send_iv, sa->encrypt->iv_len);
}
payload = iv + sa->encrypt->iv_len;
/* copy payload */
memcpy(payload, buf, len);
/* append padding */
n = len;
for (i = 1; i <= padding; i++)
payload[n++] = i;
payload[n++] = padding; /* length of padding */
payload[n++] = 0; /* next_header */
/* encrypt the payload */
if (sa->encrypt) {
n = sa->encrypt->encrypt(sa->encrypt_context,
payload, payload, n, iv,
aad, 2 * sizeof(uint32_t));
if (n < 0) {
errno = EMSGSIZE;
return -1;
}
n += 2 * sizeof(uint32_t) + sa->encrypt->iv_len;
}
/* add message authentication */
if (sa->authenticate) {
icv = out + n;
sa->authenticate->auth(sa->authenticate_context, out, n, icv);
n += sa->authenticate->icv_len;
}
buf = out;
to = (struct sockaddr *)&sa->peer;
tolen = sa->peerlen;
send_packet:
padding = n - len;
n = sendto(s, buf, n, flags, to, tolen);
#ifdef __linux__
if (n == -1 && errno == ECONNREFUSED)
{
/* On linux ECONNREFUSED is a result of a previous sendto
* triggering an ICMP bad host/port response. This
* behaviour seems to be required by RFC1122, but in
* practice this is not implemented by other UDP stacks.
* We retry the send, because the failing host was possibly
* not the one we tried to send to this time. --JH
*/
n = sendto(s, buf, n, 0, to, tolen);
}
#endif
n -= padding;
if (n < -1) n = -1;
return n;
}
|