osx-attr: add plugin installing config attributes using SystemConfiguration
[strongswan.git] / src / libcharon / plugins / osx_attr / osx_attr_handler.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "osx_attr_handler.h"
17
18 #include <networking/host.h>
19 #include <utils/debug.h>
20
21 #include <SystemConfiguration/SCDynamicStore.h>
22
23 typedef struct private_osx_attr_handler_t private_osx_attr_handler_t;
24
25 /**
26 * Private data of an osx_attr_handler_t object.
27 */
28 struct private_osx_attr_handler_t {
29
30 /**
31 * Public interface
32 */
33 osx_attr_handler_t public;
34 };
35
36 /**
37 * Create a path to the DNS configuration of the Primary IPv4 Service
38 */
39 static CFStringRef create_dns_path(SCDynamicStoreRef store)
40 {
41 CFStringRef service, path = NULL;
42 CFDictionaryRef dict;
43
44 /* get primary service */
45 dict = SCDynamicStoreCopyValue(store, CFSTR("State:/Network/Global/IPv4"));
46 if (dict)
47 {
48 service = CFDictionaryGetValue(dict, CFSTR("PrimaryService"));
49 if (service)
50 {
51 path = CFStringCreateWithFormat(NULL, NULL,
52 CFSTR("State:/Network/Service/%@/DNS"), service);
53 }
54 else
55 {
56 DBG1(DBG_CFG, "SystemConfiguration PrimaryService not known");
57 }
58 CFRelease(dict);
59 }
60 else
61 {
62 DBG1(DBG_CFG, "getting global IPv4 SystemConfiguration failed");
63 }
64 return path;
65 }
66
67 /**
68 * Create a mutable dictionary from path, a new one if not found
69 */
70 static CFMutableDictionaryRef get_dictionary(SCDynamicStoreRef store,
71 CFStringRef path)
72 {
73 CFDictionaryRef dict;
74 CFMutableDictionaryRef mut = NULL;
75
76 dict = SCDynamicStoreCopyValue(store, path);
77 if (dict)
78 {
79 if (CFGetTypeID(dict) == CFDictionaryGetTypeID())
80 {
81 mut = CFDictionaryCreateMutableCopy(NULL, 0, dict);
82 }
83 CFRelease(dict);
84 }
85 if (!mut)
86 {
87 mut = CFDictionaryCreateMutable(NULL, 0,
88 &kCFTypeDictionaryKeyCallBacks,
89 &kCFTypeDictionaryValueCallBacks);
90 }
91 return mut;
92 }
93
94 /**
95 * Create a mutable array from dictionary path, a new one if not found
96 */
97 static CFMutableArrayRef get_array_from_dict(CFDictionaryRef dict,
98 CFStringRef name)
99 {
100 CFArrayRef arr;
101
102 arr = CFDictionaryGetValue(dict, name);
103 if (arr && CFGetTypeID(arr) == CFArrayGetTypeID())
104 {
105 return CFArrayCreateMutableCopy(NULL, 0, arr);
106 }
107 return CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
108 }
109
110 /**
111 * Add/Remove a DNS server to the configuration
112 */
113 static bool manage_dns(int family, chunk_t data, bool add)
114 {
115 SCDynamicStoreRef store;
116 CFStringRef path, dns;
117 CFMutableArrayRef arr;
118 CFMutableDictionaryRef dict;
119 CFIndex i;
120 host_t *server;
121 char buf[64];
122 bool success = FALSE;
123
124 server = host_create_from_chunk(family, data, 0);
125 if (!server)
126 {
127 return FALSE;
128 }
129 snprintf(buf, sizeof(buf), "%H", server);
130 server->destroy(server);
131
132 store = SCDynamicStoreCreate(NULL, CFSTR("osx-attr"), NULL, NULL);
133 path = create_dns_path(store);
134 if (path)
135 {
136 dict = get_dictionary(store, path);
137 arr = get_array_from_dict(dict, CFSTR("ServerAddresses"));
138 dns = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8);
139 if (add)
140 {
141 DBG1(DBG_CFG, "installing %s as DNS server", buf);
142 CFArrayInsertValueAtIndex(arr, 0, dns);
143 }
144 else
145 {
146 i = CFArrayGetFirstIndexOfValue(arr,
147 CFRangeMake(0, CFArrayGetCount(arr)), dns);
148 if (i >= 0)
149 {
150 DBG1(DBG_CFG, "removing %s from DNS servers (%d)", buf, i);
151 CFArrayRemoveValueAtIndex(arr, i);
152 }
153 }
154 CFRelease(dns);
155 CFDictionarySetValue(dict, CFSTR("ServerAddresses"), arr);
156 CFRelease(arr);
157
158 success = SCDynamicStoreSetValue(store, path, dict);
159 CFRelease(dict);
160 CFRelease(path);
161 }
162 CFRelease(store);
163
164 if (!success)
165 {
166 DBG1(DBG_CFG, "adding DNS server to SystemConfiguration failed");
167 }
168 return success;
169 }
170
171 METHOD(attribute_handler_t, handle, bool,
172 private_osx_attr_handler_t *this, identification_t *id,
173 configuration_attribute_type_t type, chunk_t data)
174 {
175 switch (type)
176 {
177 case INTERNAL_IP4_DNS:
178 return manage_dns(AF_INET, data, TRUE);
179 default:
180 return FALSE;
181 }
182 }
183
184 METHOD(attribute_handler_t, release, void,
185 private_osx_attr_handler_t *this, identification_t *server,
186 configuration_attribute_type_t type, chunk_t data)
187 {
188 switch (type)
189 {
190 case INTERNAL_IP4_DNS:
191 manage_dns(AF_INET, data, FALSE);
192 break;
193 default:
194 break;
195 }
196 }
197
198 METHOD(enumerator_t, enumerate_dns, bool,
199 enumerator_t *this, configuration_attribute_type_t *type, chunk_t *data)
200 {
201 *type = INTERNAL_IP4_DNS;
202 *data = chunk_empty;
203 /* stop enumeration */
204 this->enumerate = (void*)return_false;
205 return TRUE;
206 }
207
208 METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
209 private_osx_attr_handler_t *this, identification_t *id,
210 linked_list_t *vips)
211 {
212 enumerator_t *enumerator;
213
214 INIT(enumerator,
215 .enumerate = (void*)_enumerate_dns,
216 .destroy = (void*)free,
217 );
218 return enumerator;
219 }
220
221 METHOD(osx_attr_handler_t, destroy, void,
222 private_osx_attr_handler_t *this)
223 {
224 free(this);
225 }
226
227 /**
228 * See header
229 */
230 osx_attr_handler_t *osx_attr_handler_create()
231 {
232 private_osx_attr_handler_t *this;
233
234 INIT(this,
235 .public = {
236 .handler = {
237 .handle = _handle,
238 .release = _release,
239 .create_attribute_enumerator = _create_attribute_enumerator,
240 },
241 .destroy = _destroy,
242 },
243 );
244
245 return &this->public;
246 }