2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <xpc/private.h>
29 #include "tzlink_internal.h"
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 *);
39 dispatch_queue_t queue
;
40 xpc_connection_t listener
;
44 queue
= dispatch_queue_create(TZLINK_SERVICE_NAME
, NULL
);
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
) {
52 xpc_connection_set_target_queue(peer
, queue
);
53 xpc_connection_set_event_handler(peer
, ^(xpc_object_t request
) {
58 if (xpc_get_type(request
) != XPC_TYPE_DICTIONARY
) {
62 if (peer_entitled(peer
)) {
63 tz
= xpc_dictionary_get_string(request
, TZLINK_KEY_REQUEST_TIMEZONE
);
64 error
= tz
? set_timezone(tz
) : EINVAL
;
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
);
74 xpc_connection_resume(peer
);
76 xpc_connection_resume(listener
);
82 peer_entitled(xpc_connection_t peer
)
89 val
= xpc_connection_copy_entitlement_value(peer
, TZLINK_ENTITLEMENT
);
91 entitled
= xpc_bool_get_value(val
);
99 set_timezone(const char *tz
)
101 char srcpath
[PATH_MAX
];
104 error
= build_source_path(srcpath
, sizeof(srcpath
), tz
);
109 error
= validate_source_path(srcpath
);
114 (void)unlink(TZDEFAULT
);
115 if (symlink(srcpath
, TZDEFAULT
) != 0) {
116 return errno
? errno
: EFAULT
;
120 * notifyd posts "com.apple.system.timezone" automatically,
121 * but we also need post this. Sigh.
123 (void)notify_post("SignificantTimeChangeNotification");
128 /* Create path from input. */
130 build_source_path(char *path
, size_t size
, const char *tz
)
136 if (strlcpy(path
, TZDIR
, size
) >= size
) {
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] == '.') {
149 if (strlcat(path
, "/", size
) >= size
) {
150 error
= ENAMETOOLONG
;
153 if (strlcat(path
, pathcomp
, size
) >= size
) {
154 error
= ENAMETOOLONG
;
165 validate_source_path(const char *path
)
169 if (lstat(path
, &sb
) != 0) {
170 return errno
? errno
: EFAULT
;
173 /* Ensure that the time zone file is... */
175 /* ... a regular file. */
176 if (!S_ISREG(sb
.st_mode
)) {
180 /* ... owned by root:wheel */
181 if (sb
.st_uid
!= 0 || sb
.st_gid
!= 0) {
186 if ((sb
.st_mode
& ACCESSPERMS
) != 0644) {
190 /* ... is a real tzfile (starts with TZif) */
191 // TODO: Validate contents of file.