File: secure_sendto.c

package info (click to toggle)
rpc2 2.4%2Bdebian-1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 2,832 kB
  • ctags: 2,657
  • sloc: ansic: 19,563; sh: 9,022; lex: 437; yacc: 416; makefile: 127; asm: 35; perl: 17
file content (141 lines) | stat: -rw-r--r-- 3,748 bytes parent folder | download | duplicates (2)
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;
}