1/*
2    File: PrivilegedOperations.c
3
4    Abstract: Interface to "ddnswriteconfig" setuid root tool.
5
6    Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved.
7
8    Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
9    ("Apple") in consideration of your agreement to the following terms, and your
10    use, installation, modification or redistribution of this Apple software
11    constitutes acceptance of these terms.  If you do not agree with these terms,
12    please do not use, install, modify or redistribute this Apple software.
13
14    In consideration of your agreement to abide by the following terms, and subject
15    to these terms, Apple grants you a personal, non-exclusive license, under Apple's
16    copyrights in this original Apple software (the "Apple Software"), to use,
17    reproduce, modify and redistribute the Apple Software, with or without
18    modifications, in source and/or binary forms; provided that if you redistribute
19    the Apple Software in its entirety and without modifications, you must retain
20    this notice and the following text and disclaimers in all such redistributions of
21    the Apple Software.  Neither the name, trademarks, service marks or logos of
22    Apple Computer, Inc. may be used to endorse or promote products derived from the
23    Apple Software without specific prior written permission from Apple.  Except as
24    expressly stated in this notice, no other rights or licenses, express or implied,
25    are granted by Apple herein, including but not limited to any patent rights that
26    may be infringed by your derivative works or by other works in which the Apple
27    Software may be incorporated.
28
29    The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
30    WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
31    WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32    PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
33    COMBINATION WITH YOUR PRODUCTS.
34
35    IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
36    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
37    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38    ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
39    OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
40    (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
41    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44#include "PrivilegedOperations.h"
45#include "ConfigurationAuthority.h"
46#include <CoreFoundation/CoreFoundation.h>
47#include <SystemConfiguration/SystemConfiguration.h>
48#include <stdio.h>
49#include <stdint.h>
50#include <stdlib.h>
51#include <unistd.h>
52#include <sys/wait.h>
53#include <AssertMacros.h>
54#include <Security/Security.h>
55
56Boolean gToolApproved = false;
57
58static pid_t    execTool(const char *args[])
59// fork/exec and return new pid
60{
61    pid_t child;
62
63    child = vfork();
64    if (child == 0)
65    {
66        execv(args[0], (char *const *)args);
67        printf("exec of %s failed; errno = %d\n", args[0], errno);
68        _exit(-1);      // exec failed
69    }
70    else
71        return child;
72}
73
74OSStatus EnsureToolInstalled(void)
75// Make sure that the tool is installed in the right place, with the right privs, and the right version.
76{
77    CFURLRef bundleURL;
78    pid_t toolPID;
79    int status;
80    OSStatus err = noErr;
81    const char      *args[] = { kToolPath, "0", "V", NULL };
82    char toolSourcePath[PATH_MAX] = {};
83    char toolInstallerPath[PATH_MAX] = {};
84
85    if (gToolApproved)
86        return noErr;
87
88    // Check version of installed tool
89    toolPID = execTool(args);
90    if (toolPID > 0)
91    {
92        waitpid(toolPID, &status, 0);
93        if (WIFEXITED(status) && WEXITSTATUS(status) == PRIV_OP_TOOL_VERS)
94            return noErr;
95    }
96
97    // Locate our in-bundle copy of privop tool
98    bundleURL = CFBundleCopyBundleURL(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.preference.bonjour")) );
99    if (bundleURL != NULL)
100    {
101        CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolSourcePath, sizeof toolSourcePath);
102        if (strlcat(toolSourcePath,    "/Contents/Resources/" kToolName,      sizeof toolSourcePath   ) >= sizeof toolSourcePath   ) return(-1);
103        CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolInstallerPath, sizeof toolInstallerPath);
104        if (strlcat(toolInstallerPath, "/Contents/Resources/" kToolInstaller, sizeof toolInstallerPath) >= sizeof toolInstallerPath) return(-1);
105    }
106    else
107        return coreFoundationUnknownErr;
108
109    // Obtain authorization and run in-bundle copy as root to install it
110    {
111        AuthorizationItem aewpRight = { kAuthorizationRightExecute, strlen(toolInstallerPath), toolInstallerPath, 0 };
112        AuthorizationItemSet rights = { 1, &aewpRight };
113        AuthorizationRef authRef;
114
115        err = AuthorizationCreate(&rights, (AuthorizationEnvironment*) NULL,
116                                  kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights |
117                                  kAuthorizationFlagPreAuthorize, &authRef);
118        if (err == noErr)
119        {
120            char *installerargs[] = { toolSourcePath, NULL };
121            err = AuthorizationExecuteWithPrivileges(authRef, toolInstallerPath, 0, installerargs, (FILE**) NULL);
122            if (err == noErr) {
123                int pid = wait(&status);
124                if (pid > 0 && WIFEXITED(status)) {
125                    err = WEXITSTATUS(status);
126                    if (err == noErr) {
127                        gToolApproved = true;
128                    }
129                } else {
130                    err = -1;
131                }
132            }
133            (void) AuthorizationFree(authRef, kAuthorizationFlagDefaults);
134        }
135    }
136
137    return err;
138}
139
140
141static OSStatus ExecWithCmdAndParam(const char *subCmd, CFDataRef paramData)
142// Execute our privop tool with the supplied subCmd and parameter
143{
144    OSStatus err = noErr;
145    int commFD, dataLen;
146    u_int32_t len;
147    pid_t child;
148    char fileNum[16];
149    UInt8                   *buff;
150    const char              *args[] = { kToolPath, NULL, "A", NULL, NULL };
151    AuthorizationExternalForm authExt;
152
153    err = ExternalizeAuthority(&authExt);
154    require_noerr(err, AuthFailed);
155
156    dataLen = CFDataGetLength(paramData);
157    buff = (UInt8*) malloc(dataLen * sizeof(UInt8));
158    require_action(buff != NULL, AllocBuffFailed, err=memFullErr;);
159    {
160        CFRange all = { 0, dataLen };
161        CFDataGetBytes(paramData, all, buff);
162    }
163
164    commFD = fileno(tmpfile());
165    sprintf(fileNum, "%d", commFD);
166    args[1] = fileNum;
167    args[3] = subCmd;
168
169    // write authority to pipe
170    len = 0;    // tag, unused
171    write(commFD, &len, sizeof len);
172    len = sizeof authExt;   // len
173    write(commFD, &len, sizeof len);
174    write(commFD, &authExt, len);
175
176    // write parameter to pipe
177    len = 0;    // tag, unused
178    write(commFD, &len, sizeof len);
179    len = dataLen;  // len
180    write(commFD, &len, sizeof len);
181    write(commFD, buff, len);
182
183    child = execTool(args);
184    if (child > 0) {
185        int status;
186        waitpid(child, &status, 0);
187        if (WIFEXITED(status))
188            err = WEXITSTATUS(status);
189        //fprintf(stderr, "child exited; status = %d (%ld)\n", status, err);
190    }
191
192    close(commFD);
193
194    free(buff);
195AllocBuffFailed:
196AuthFailed:
197    return err;
198}
199
200OSStatus
201WriteBrowseDomain(CFDataRef domainArrayData)
202{
203    if (!CurrentlyAuthorized())
204        return authFailErr;
205    return ExecWithCmdAndParam("Wb", domainArrayData);
206}
207
208OSStatus
209WriteRegistrationDomain(CFDataRef domainArrayData)
210{
211    if (!CurrentlyAuthorized())
212        return authFailErr;
213    return ExecWithCmdAndParam("Wd", domainArrayData);
214}
215
216OSStatus
217WriteHostname(CFDataRef domainArrayData)
218{
219    if (!CurrentlyAuthorized())
220        return authFailErr;
221    return ExecWithCmdAndParam("Wh", domainArrayData);
222}
223
224OSStatus
225SetKeyForDomain(CFDataRef secretData)
226{
227    if (!CurrentlyAuthorized())
228        return authFailErr;
229    return ExecWithCmdAndParam("Wk", secretData);
230}
231