1 /* srp-ioloop.c 2 * 3 * Copyright (c) 2019 Apple Computer, Inc. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * srp host API implementation for Posix using ioloop primitives. 18 */ 19 20 #include <stdio.h> 21 #include <arpa/inet.h> 22 #include <string.h> 23 #include <stdlib.h> 24 #include <unistd.h> 25 #include <dns_sd.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 29 #include "srp.h" 30 #include "srp-api.h" 31 #include "dns-msg.h" 32 #include "srp-crypto.h" 33 #include "ioloop.h" 34 35 bool 36 srp_load_file_data(void *host_context, const char *filename, uint8_t *buffer, uint16_t *length, uint16_t buffer_size) 37 { 38 off_t flen; 39 ssize_t len; 40 int file; 41 (void)host_context; 42 43 file = open(filename, O_RDONLY); 44 if (file < 0) { 45 ERROR("srp_load_file_data: %s: open: %s", filename, strerror(errno)); 46 return false; 47 } 48 49 // Get the length of the file. 50 flen = lseek(file, 0, SEEK_END); 51 lseek(file, 0, SEEK_SET); 52 if (flen > buffer_size) { 53 ERROR("srp_load_file_data: %s: lseek: %s", filename, strerror(errno)); 54 close(file); 55 return false; 56 } 57 len = read(file, buffer, (size_t)flen); // Note: flen always positive, no loss of precision. 58 if (len < 0 || len != flen) { 59 if (len < 0) { 60 ERROR("srp_load_file_data: %s: read: %s", filename, strerror(errno)); 61 } else { 62 ERROR("srp_load_file_data: %s: short read %d out of %d", filename, (int)len, (int)flen); 63 } 64 close(file); 65 return false; 66 } 67 close(file); 68 *length = (uint16_t)len; 69 return true; 70 } 71 72 bool 73 srp_store_file_data(void *host_context, const char *filename, uint8_t *buffer, uint16_t length) 74 { 75 ssize_t len; 76 int file; 77 (void)host_context; 78 file = open(filename, O_WRONLY | O_CREAT, 0600); 79 if (file < 0) { 80 ERROR("srp_store_file_data: %s: %s", filename, strerror(errno)); 81 return false; 82 } 83 len = write(file, buffer, length); 84 if (len < 0 || len != length) { 85 if (len < 0) { 86 ERROR("srp_store_file_data: " PUB_S_SRP ": " PUB_S_SRP, filename, strerror(errno)); 87 } else { 88 ERROR("srp_store_file_data: short write %d out of %d on file " PUB_S_SRP, (int)len, (int)length, filename); 89 } 90 unlink(filename); 91 close(file); 92 return false; 93 } 94 close(file); 95 return true; 96 } 97 98 99 bool 100 srp_get_last_server(uint16_t *NONNULL rrtype, uint8_t *NONNULL rdata, uint16_t rdlim, 101 uint8_t *NONNULL port, void *NULLABLE host_context) 102 { 103 uint8_t buffer[22]; 104 unsigned offset = 0; 105 uint16_t length; 106 uint16_t rdlength; 107 108 if (!srp_load_file_data(host_context, "/var/run/srp-last-server", buffer, &length, sizeof(buffer))) { 109 return false; 110 } 111 if (length < 10) { // rrtype + rdlength + ipv4 address + port 112 ERROR("srp_get_last_server: stored server data is too short: %d", length); 113 return false; 114 } 115 *rrtype = (((uint16_t)buffer[offset]) << 8) | buffer[offset + 1]; 116 offset += 2; 117 rdlength = (((uint16_t)buffer[offset]) << 8) | buffer[offset + 1]; 118 offset += 2; 119 if ((*rrtype == dns_rrtype_a && rdlength != 4) || (*rrtype == dns_rrtype_aaaa && rdlength != 16)) { 120 ERROR("srp_get_last_server: invalid rdlength %d for %s record", 121 rdlength, *rrtype == dns_rrtype_a ? "A" : "AAAA"); 122 return false; 123 } 124 if (length < rdlength + 6) { // rrtype + rdlength + address + port 125 ERROR("srp_get_last_server: stored server data length %d is too short", length); 126 return false; 127 } 128 if (rdlength > rdlim) { 129 ERROR("srp_get_last_server: no space for %s data in provided buffer size %d", 130 *rrtype == dns_rrtype_a ? "A" : "AAAA", rdlim); 131 return false; 132 } 133 memcpy(rdata, &buffer[offset], rdlength); 134 offset += rdlength; 135 memcpy(port, &buffer[offset], 2); 136 return true; 137 } 138 139 bool 140 srp_save_last_server(uint16_t rrtype, uint8_t *NONNULL rdata, uint16_t rdlength, 141 uint8_t *NONNULL port, void *NULLABLE host_context) 142 { 143 dns_towire_state_t towire; 144 uint8_t buffer[24]; 145 size_t length; 146 memset(&towire, 0, sizeof(towire)); 147 towire.p = buffer; 148 towire.lim = towire.p + sizeof(buffer); 149 150 if (rdlength != 4 && rdlength != 16) { 151 ERROR("srp_save_last_server: invalid IP address length %d", rdlength); 152 return false; 153 } 154 dns_u16_to_wire(&towire, rrtype); 155 dns_u16_to_wire(&towire, rdlength); 156 dns_rdata_raw_data_to_wire(&towire, rdata, rdlength); 157 dns_rdata_raw_data_to_wire(&towire, port, 2); 158 159 if (towire.error) { 160 ERROR("srp_save_last_server: " PUB_S_SRP " at %d (%p:%p:%p) while constructing output buffer", 161 strerror(towire.error), towire.line, towire.p, towire.lim, buffer); 162 return false; 163 } 164 165 length = towire.p - buffer; 166 if (!srp_store_file_data(host_context, "/var/run/srp-last-server", buffer, length)) { 167 return false; 168 } 169 return true; 170 } 171 172 #ifdef SRP_CRYPTO_MBEDTLS 173 int 174 srp_load_key_data(void *host_context, const char *key_name, uint8_t *buffer, uint16_t *length, uint16_t buffer_size) 175 { 176 if (srp_load_file_data(host_context, key_name, buffer, length, buffer_size)) { 177 return kDNSServiceErr_NoError; 178 } 179 return kDNSServiceErr_NoSuchKey; 180 } 181 182 int 183 srp_store_key_data(void *host_context, const char *key_name, uint8_t *buffer, uint16_t length) 184 { 185 if (!srp_store_file_data(host_context, key_name, buffer, length)) { 186 return kDNSServiceErr_Unknown; 187 } 188 return kDNSServiceErr_NoError; 189 } 190 191 int 192 srp_remove_key_file(void *host_context, const char *key_name) 193 { 194 if (unlink(key_name) < 0) { 195 return kDNSServiceErr_Unknown; 196 } 197 return kDNSServiceErr_NoError; 198 } 199 #endif // SRP_CRYPTO_MBEDTLS_INTERNAL 200