Version 4 of tclAuthorization

Updated 2005-02-01 00:43:42

Critcl wrapper for Mac OS X Authorization services, only the AuthorizationExecuteWithPrivileges() API for now, c.f. [L1 ] for details.

[ Daniel Steffen 24/01/05 ]


 #!/bin/sh
 # #######################################################################
 #
 #  tclAuthorization.tcl
 #
 #  Critcl wrapper for Mac OS X Authorization services:
 #   - AuthorizationExecuteWithPrivileges() only for now
 #
 #  Process this file with 'critcl -pkg' to build a loadable package (or
 #  simply source this file if [package require critcl] and a compiler
 #  are available at deployment).
 #
 #
 #  Author: Daniel A. Steffen
 #  E-mail: <[email protected]>
 #    mail: Mathematics Departement
 #          Macquarie University NSW 2109 Australia
 #     www: <http://www.maths.mq.edu.au/~steffen/>
 #
 # RCS: @(#) $Id: 13401,v 1.5 2005-02-01 07:01:03 jcw Exp $
 #
 # BSD License: c.f. <http://www.opensource.org/licenses/bsd-license>
 #
 # Copyright (c) 2005, Daniel A. Steffen <[email protected]>
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or
 # without modification, are permitted provided that the following
 # conditions are met:
 #
 #   * Redistributions of source code must retain the above
 #     copyright notice, this list of conditions and the
 #     following disclaimer.
 #
 #   * Redistributions in binary form must reproduce the above
 #     copyright notice, this list of conditions and the following
 #     disclaimer in the documentation and/or other materials
 #     provided with the distribution.
 #
 #   * Neither the name of Macquarie University nor the names of its
 #     contributors may be used to endorse or promote products derived
 #     from this software without specific prior written permission.
 #
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MACQUARIE
 # UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 # DAMAGE.
 #
 # #######################################################################
 # \
 exec tclsh "$0" "$@"

 package require critcl
 if {![::critcl::compiling]} {error "No compiler found"}

 #---------------------------------------------------------------------------------------------------

 package provide tclAuthorization 1.0

 namespace eval tclAuthorization {

 lappend ::critcl::v::compile -framework Security

 ::critcl::ccode {
     #include <stdio.h>
     #include <unistd.h>
     #include <Security/Authorization.h>
     #include <Security/AuthorizationTags.h>

     typedef struct {
         OSStatus code;
         const char *name;
     } AuthErrId;

     static AuthErrId authErrIds[] = {
         { errAuthorizationSuccess,
                 "AuthorizationSuccess: The operation completed successfully." },
         { errAuthorizationInvalidSet,
                 "AuthorizationInvalidSet: The set parameter is invalid." },
         { errAuthorizationInvalidRef,
                 "AuthorizationInvalidRef: The authorization parameter is invalid." },
         { errAuthorizationInvalidTag,
                 "AuthorizationInvalidTag: The tag parameter is invalid." },
         { errAuthorizationInvalidPointer,
                 "AuthorizationInvalidPointer: The authorizedRights parameter is invalid." },
         { errAuthorizationDenied,
                 "AuthorizationDenied: The authorization was denied." },
         { errAuthorizationCanceled,
                 "AuthorizationCanceled: The authorization was cancelled by the user." },
         { errAuthorizationInteractionNotAllowed,
                 "AuthorizationInteractionNotAllowed: The authorization was denied since no user interaction was possible." },
         { errAuthorizationInternal,
                 "AuthorizationInternal: something else went wrong" },
         { errAuthorizationExternalizeNotAllowed,
                 "AuthorizationExternalizeNotAllowed: authorization externalization denied" },
         { errAuthorizationInternalizeNotAllowed,
                 "AuthorizationInternalizeNotAllowed: authorization internalization denied" },
         { errAuthorizationInvalidFlags,
                 "AuthorizationInvalidFlags: invalid option flag(s)" },
         { errAuthorizationToolExecuteFailure,
                 "AuthorizationToolExecuteFailure: cannot execute privileged tool" },
         { errAuthorizationToolEnvironmentError,
                 "AuthorizationToolEnvironmentError: privileged tool environment error" },
         { errAuthorizationBadAddress,
                 "AuthorizationBadAddress: invalid socket address requested" },
         { 0, NULL }
     };

     static const char *AuthErrTxt(OSStatus status) {
         AuthErrId *errId = &authErrIds[0];
         while (errId->name && errId->code != status) { errId++; }
         return errId->name;
     }
 }

 #---------------------------------------------------------------------------------------------------
 #
 # tclAuthorization::executeWithPrivileges /path/to/executable ?arg ...?
 #
 #   this command takes an absolute path to an executable along with optional arguments for it,
 #   and runs that executable with an effective user id of root, after presenting a standard
 #   Mac OS X authorization dialog (whose prompt shows the given executable and arguments).
 #
 #   If sucessful, the command returns a channel connected to stdin and stdout of the
 #   executable (stderr is unavailable). That channel needs be closed manually once the
 #   executable has exited or is no longer needed.
 #
 #---------------------------------------------------------------------------------------------------

 ::critcl::ccommand executeWithPrivileges {ClientData interp objc objv} {
     int i, result = TCL_ERROR;
     Tcl_DString ds;
     Tcl_Obj *pathPtr = NULL;
     const char *myPathToTool;
     char** myArguments = NULL;
     FILE *myCommunicationsPipe;
     Tcl_Channel chan;
     OSStatus myStatus;

     AuthorizationItem myAuthorizationExecuteRight = {kAuthorizationRightExecute, 0, NULL, 0};
     AuthorizationRights myAuthorizationRights = {1, &myAuthorizationExecuteRight};
     AuthorizationItem myAuthorizationPrompt = {kAuthorizationEnvironmentPrompt, 0, NULL, 0};
     AuthorizationEnvironment myAuthorizationEnvironment = {1, &myAuthorizationPrompt};
     AuthorizationRef myAuthorizationRef = NULL;

     while (1) {
         Tcl_DStringInit(&ds);
         if (objc < 2) {
             Tcl_WrongNumArgs(interp, 1, objv, "executable ?arg...?");
             break;
         }

         pathPtr = Tcl_FSGetNormalizedPath(interp, objv[1]);
         if (!pathPtr) {
             Tcl_AppendResult(interp, "could not normalize path to executable \"",
                     Tcl_GetString(objv[1]), "\"", NULL);
             break;
         }
         Tcl_IncrRefCount(pathPtr);

         if (Tcl_FSAccess(pathPtr, X_OK)) {
             Tcl_AppendResult(interp, "command \"", Tcl_GetString(objv[1]),
                     "\" is not executable: ", Tcl_PosixError(interp), NULL);
             break;
         }

         myPathToTool = Tcl_FSGetNativePath(pathPtr);
         if (!myPathToTool) {
             Tcl_AppendResult(interp, "could not get native path to executable \"",
                     Tcl_GetString(objv[1]), "\": ", Tcl_PosixError(interp), NULL);
             break;
         }

         Tcl_DStringAppend(&ds, "Authorize execution of command\n\t", -1);
         Tcl_DStringAppendElement(&ds, myPathToTool);
         if (objc > 2) {
             int n = objc - 2;
             myArguments = (char**) ckalloc((n + 1) * sizeof(char*));
             for (i = 0; i < n; i++) {
                 myArguments[i] = Tcl_GetString(objv[i+2]);
                 Tcl_DStringAppendElement(&ds, myArguments[i]);
             }
             myArguments[i] = NULL;
         }
         Tcl_DStringAppend(&ds, "\n", 1);
         myAuthorizationPrompt.value = (void*) Tcl_DStringValue(&ds);
         myAuthorizationPrompt.valueLength = Tcl_DStringLength(&ds);

         myAuthorizationExecuteRight.value = (void*) myPathToTool;
         myAuthorizationExecuteRight.valueLength = strlen(myPathToTool);

         myStatus = AuthorizationCreate(&myAuthorizationRights, &myAuthorizationEnvironment,
                 kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights,
                 &myAuthorizationRef);
         if (myStatus != errAuthorizationSuccess) {
             Tcl_AppendResult(interp, "could not authorize, ", AuthErrTxt(myStatus), NULL);
             break;
         }

         myStatus = AuthorizationExecuteWithPrivileges(myAuthorizationRef, myPathToTool, 
                 kAuthorizationFlagDefaults, myArguments, &myCommunicationsPipe);
         if (myStatus != errAuthorizationSuccess) {
             Tcl_AppendResult(interp, "could not execute command, ", AuthErrTxt(myStatus), NULL);
             break;
         }

         chan = Tcl_MakeFileChannel((void*)fileno(myCommunicationsPipe),
                 TCL_READABLE|TCL_WRITABLE);
         if (!chan) {
             Tcl_AppendResult(interp, "could not make tcl channel for I/O pipe", NULL);
             break;
         }

         Tcl_RegisterChannel(interp, chan);
         Tcl_SetResult(interp, (char*)Tcl_GetChannelName(chan), TCL_VOLATILE);
         result = TCL_OK;
         break;

     }
     if(myAuthorizationRef) {
         AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDestroyRights);
     }
     if (pathPtr) {
         Tcl_DecrRefCount(pathPtr);
     }
     if (myArguments) {
        ckfree((char*)myArguments);
     }
     Tcl_DStringFree(&ds);
     return result;

 }

 }
 #---------------------------------------------------------------------------------------------------

RLH - Since there are a couple of these by DAS, maybe we could create a single listing for Tcl libs for OSX? You can remove this comment if the suggestion is implemented or rejected.


Category Package