]> git.saurik.com Git - apple/libutil.git/blob - tzlinkd/tzlinkd.c
libutil-34.tar.gz
[apple/libutil.git] / tzlinkd / tzlinkd.c
1 /*
2 * Copyright (c) 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <xpc/xpc.h>
25 #include <xpc/private.h>
26 #include <notify.h>
27 #include <tzfile.h>
28
29 #include "tzlink_internal.h"
30
31 static bool peer_entitled(xpc_connection_t);
32 static int set_timezone(const char *);
33 static int build_source_path(char *, size_t, const char *);
34 static int validate_source_path(const char *);
35
36 int
37 main(void)
38 {
39 dispatch_queue_t queue;
40 xpc_connection_t listener;
41
42 xpc_track_activity();
43
44 queue = dispatch_queue_create(TZLINK_SERVICE_NAME, NULL);
45
46 listener = xpc_connection_create_mach_service(TZLINK_SERVICE_NAME, queue, XPC_CONNECTION_MACH_SERVICE_LISTENER);
47 xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
48 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION) {
49 return;
50 }
51
52 xpc_connection_set_target_queue(peer, queue);
53 xpc_connection_set_event_handler(peer, ^(xpc_object_t request) {
54 xpc_object_t reply;
55 const char *tz;
56 int error;
57
58 if (xpc_get_type(request) != XPC_TYPE_DICTIONARY) {
59 return;
60 }
61
62 if (peer_entitled(peer)) {
63 tz = xpc_dictionary_get_string(request, TZLINK_KEY_REQUEST_TIMEZONE);
64 error = tz ? set_timezone(tz) : EINVAL;
65 } else {
66 error = EPERM;
67 }
68
69 reply = xpc_dictionary_create_reply(request);
70 xpc_dictionary_set_uint64(reply, TZLINK_KEY_REPLY_ERROR, (uint64_t)error);
71 xpc_connection_send_message(peer, reply);
72 xpc_release(reply);
73 });
74 xpc_connection_resume(peer);
75 });
76 xpc_connection_resume(listener);
77
78 dispatch_main();
79 }
80
81 static bool
82 peer_entitled(xpc_connection_t peer)
83 {
84 xpc_object_t val;
85 bool entitled;
86
87 entitled = false;
88
89 val = xpc_connection_copy_entitlement_value(peer, TZLINK_ENTITLEMENT);
90 if (val != NULL) {
91 entitled = xpc_bool_get_value(val);
92 xpc_release(val);
93 }
94
95 return entitled;
96 }
97
98 static int
99 set_timezone(const char *tz)
100 {
101 char srcpath[PATH_MAX];
102 int error;
103
104 error = build_source_path(srcpath, sizeof(srcpath), tz);
105 if (error != 0) {
106 return error;
107 }
108
109 error = validate_source_path(srcpath);
110 if (error != 0) {
111 return error;
112 }
113
114 (void)unlink(TZDEFAULT);
115 if (symlink(srcpath, TZDEFAULT) != 0) {
116 return errno ? errno : EFAULT;
117 }
118
119 /*
120 * notifyd posts "com.apple.system.timezone" automatically,
121 * but we also need post this. Sigh.
122 */
123 (void)notify_post("SignificantTimeChangeNotification");
124
125 return 0;
126 }
127
128 /* Create path from input. */
129 static int
130 build_source_path(char *path, size_t size, const char *tz)
131 {
132 char *str, *str0;
133 char *pathcomp;
134 int error;
135
136 if (strlcpy(path, TZDIR, size) >= size) {
137 return ENAMETOOLONG;
138 }
139
140 error = 0;
141
142 /* Attempt to validate the input; construct a clean path as we go. */
143 str0 = str = strdup(tz);
144 while ((pathcomp = strsep(&str, "/")) != NULL) {
145 if (pathcomp[0] == '\0' || pathcomp[0] == '.') {
146 error = EINVAL;
147 break;
148 }
149 if (strlcat(path, "/", size) >= size) {
150 error = ENAMETOOLONG;
151 break;
152 }
153 if (strlcat(path, pathcomp, size) >= size) {
154 error = ENAMETOOLONG;
155 break;
156 }
157 }
158 free(str0);
159
160 return error;
161 }
162
163 /* Validate path. */
164 static int
165 validate_source_path(const char *path)
166 {
167 struct stat sb;
168
169 if (lstat(path, &sb) != 0) {
170 return errno ? errno : EFAULT;
171 }
172
173 /* Ensure that the time zone file is... */
174
175 /* ... a regular file. */
176 if (!S_ISREG(sb.st_mode)) {
177 return ENOENT;
178 }
179
180 /* ... owned by root:wheel */
181 if (sb.st_uid != 0 || sb.st_gid != 0) {
182 return ENOENT;
183 }
184
185 /* ... 0644 perms */
186 if ((sb.st_mode & ACCESSPERMS) != 0644) {
187 return ENOENT;
188 }
189
190 /* ... is a real tzfile (starts with TZif) */
191 // TODO: Validate contents of file.
192
193 return 0;
194 }