Solaris 9: libdvdread / libdvdcss needs root permission

Juergen Keil jk at tools.de
Wed Oct 23 20:32:54 CEST 2002


> Just move the USCSICMD ioctls into a separate setuid-root process,  and
> add code to libdvdcss's ioctl.c file to use that new setuid-root
> process to perform the USCSICMDs for drive authentication and title key
> access.

OK, I've implemented this one now.  A patch against libdvdcss-1.2.3 is
appended and also available here:

<URL:http://www.tools.de/solaris/libdvdcss/libdvdcss-1.2.3-patch>


Apply it like this:

  gunzip < libdvdcss-1.2.3.tar.gz | tar xf -
  cd libdvdcss-1.2.3
  patch -p1 < ../libdvdcss-1.2.3-patch
  bootstrap

There are no ABI changes for libdvdcss.so.2,  so it can be used as a direct
replacement for the official libdvdcss-1.2.3 on Solaris 9.  I also tested it
on a Solaris 2.6 and several Solaris 8 boxes,  the csstest program runs fine
on the older platform, too.

Linux and NetBSD builds are working, too.
-------------- next part --------------
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/ChangeLog libdvdcss-1.2.3-jk/ChangeLog
--- libdvdcss-1.2.3-orig/ChangeLog	Sun Oct 13 01:01:13 2002
+++ libdvdcss-1.2.3-jk/ChangeLog	Sun Oct 20 19:36:59 2002
@@ -2,6 +2,11 @@
 # ChangeLog for libdvdcss #
 #=========================#
 
+  * src/soldoor_*, src/ioctl.c: DVD ioctl support for non-root users
+    on Solaris 9 using a setuid root helper process. By J?rgen Keil
+    <jk at tools.de>.
+
+
 1.2.3
 Sun, 13 Oct 2002 01:01:03 +0200
 
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/configure.ac libdvdcss-1.2.3-jk/configure.ac
--- libdvdcss-1.2.3-orig/configure.ac	Fri Oct 11 12:03:48 2002
+++ libdvdcss-1.2.3-jk/configure.ac	Mon Oct 21 15:58:34 2002
@@ -99,10 +99,15 @@
   ])
   dnl
   dnl Solaris: sys/scsi/scsi_types.h, sys/scsi/impl/uscsi.h
+  dnl          door.h for IPC setuid root uscsi server
   dnl
   AC_CHECK_HEADER(sys/scsi/scsi_types.h,[
     AC_CHECK_HEADER(sys/scsi/impl/uscsi.h,[
       AC_DEFINE(SOLARIS_USCSI, 1, Have userspace SCSI headers.)
+      AC_CHECK_HEADER(door.h,[
+	AC_DEFINE(SOLARIS_DOORS, 1, Have door IPC.)
+	SOL_DOOR_IPC=1
+      ])
     ])
   ])
   dnl
@@ -141,6 +146,7 @@
 
 AM_CONDITIONAL(SYS_BSDI, test "x$SYS_BSDI" = "x1")
 AM_CONDITIONAL(SYS_MSVC, test "x$SYS_MSVC" = "x1")
+AM_CONDITIONAL(SOL_DOOR_IPC, test "x$SOL_DOOR_IPC" = "x1")
 
 AC_OUTPUT([Makefile src/Makefile src/dvdcss/Makefile test/Makefile debian/Makefile doc/Makefile])
 
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/Makefile.am libdvdcss-1.2.3-jk/src/Makefile.am
--- libdvdcss-1.2.3-orig/src/Makefile.am	Sat Oct 12 01:45:43 2002
+++ libdvdcss-1.2.3-jk/src/Makefile.am	Mon Oct 21 16:01:50 2002
@@ -1,5 +1,7 @@
 SUBDIRS = dvdcss
 
+AM_CPPFLAGS = -DEXEC_PREFIX=\"$(exec_prefix)\"
+
 lib_LTLIBRARIES = libdvdcss.la
 
 libdvdcss_la_SOURCES = \
@@ -9,12 +11,13 @@
 	ioctl.c ioctl.h \
 	error.c \
 	common.h \
-	$(bsdi_sources)
+	$(bsdi_sources) \
+	$(soldoor_sources)
 
 EXTRA_libdvdcss_la_SOURCES = \
 	$(bsdi_extras)
 
-libdvdcss_la_LIBADD = $(bsdi_libadd)
+libdvdcss_la_LIBADD = $(bsdi_libadd) $(soldoor_libadd)
 libdvdcss_la_LDFLAGS = -version-info 2:3:0 $(libtool_flags)
 
 bsdi_extras = bsdi_ioctl.c bsdi_dvd.h
@@ -29,3 +32,35 @@
 libtool_flags = -no-undefined
 endif
 
+if SOL_DOOR_IPC
+soldoor_sources = soldoor_ioctl.c
+soldoor_libadd = -ldoor
+
+libexec_PROGRAMS = dvdcss_door
+
+dvdcss_door_SOURCES = soldoor_server.c soldoor_uscsi.c
+dvdcss_door_LDADD = -ldoor -lpthread
+
+
+# Extra rule to set the correct permission of the setuid-root server program
+soldoor_install = soldoor-setuid-install
+soldoor-setuid-install:
+	@case `id` in \
+	uid=0*) set -x;							\
+		chown root $(DESTDIR)$(libexecdir)/dvdcss_door;		\
+		chmod 4755 $(DESTDIR)$(libexecdir)/dvdcss_door;		\
+		;;							\
+	*)	echo "==============================================";	\
+		echo "You must run 'make install' as user 'root' to ";	\
+		echo "install the setuid dvdcss_door helper program.";	\
+		echo;							\
+		echo "Or run the following commands as user 'root':";	\
+		echo "    chown root $(DESTDIR)$(libexecdir)/dvdcss_door";\
+		echo "    chmod 4755 $(DESTDIR)$(libexecdir)/dvdcss_door";\
+		echo "==============================================";	\
+		ls -l $(DESTDIR)$(libexecdir)/dvdcss_door;		\
+		;;							\
+	esac
+endif
+
+install-exec-hook:	$(soldoor_install)
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/ioctl.c libdvdcss-1.2.3-jk/src/ioctl.c
--- libdvdcss-1.2.3-orig/src/ioctl.c	Sun Oct 13 01:06:49 2002
+++ libdvdcss-1.2.3-jk/src/ioctl.c	Sun Oct 20 23:49:50 2002
@@ -75,6 +75,12 @@
 #ifdef HPUX_SCTL_IO
 #   include <sys/scsi.h>
 #endif
+#ifdef SOLDOOR_COMPILE_USCSI
+#   undef SOLARIS_DOORS
+#endif
+#ifdef SOLARIS_DOORS
+#   include "soldoor_ioctl.h"
+#endif 
 #ifdef SOLARIS_USCSI
 #   include <unistd.h>
 #   include <stropts.h>
@@ -110,7 +116,7 @@
 /*****************************************************************************
  * Local prototypes, Solaris specific
  *****************************************************************************/
-#if defined( SOLARIS_USCSI )
+#if defined( SOLARIS_USCSI ) && !defined( SOLARIS_DOORS )
 static void SolarisInitUSCSI( struct uscsi_cmd *p_sc, int i_type );
 #endif
 
@@ -185,6 +191,8 @@
 
     *pi_copyright = p_buffer[ 4 ];
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReadCopyright( i_fd, i_layer, pi_copyright );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_READ_DVD_STRUCTURE, 8 );
     
@@ -394,6 +402,8 @@
 
     memcpy( p_key, p_buffer + 4, DVD_DISCKEY_SIZE );
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReadDiscKey( i_fd, pi_agid, p_key );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_READ_DVD_STRUCTURE, DVD_DISCKEY_SIZE + 4 );
     
@@ -553,6 +563,8 @@
 
     memcpy( p_key, p_buffer + 5, DVD_KEY_SIZE );
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReadTitleKey( i_fd, pi_agid, i_pos, p_key );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_REPORT_KEY, 12 );
     
@@ -709,6 +721,8 @@
 
     *pi_agid = p_buffer[ 7 ] >> 6;
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReportAgid( i_fd, pi_agid );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_REPORT_KEY, 8 );
     
@@ -831,6 +845,8 @@
 
     memcpy( p_challenge, p_buffer + 4, DVD_CHALLENGE_SIZE );
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReportChallenge( i_fd, pi_agid, p_challenge );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_REPORT_KEY, 16 );
     
@@ -965,6 +981,8 @@
 
     *pi_asf = p_buffer[ 7 ] & 1;
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReportASF( i_fd, pi_remove_me, pi_asf );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_REPORT_KEY, 8 );
     
@@ -1098,6 +1116,8 @@
 
     memcpy( p_key, p_buffer + 4, DVD_KEY_SIZE );
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReportKey1( i_fd, pi_agid, p_key );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_REPORT_KEY, 12 );
     
@@ -1219,6 +1239,8 @@
 
     i_ret = ioctl( i_fd, SIOC_IO, &sctl_io );
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_InvalidateAgid( i_fd, pi_agid );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_REPORT_KEY, 0 );
     
@@ -1340,6 +1362,8 @@
 
     return ioctl( i_fd, SIOC_IO, &sctl_io );
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_SendChallenge( i_fd, pi_agid, p_challenge );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_SEND_KEY, 16 );
     
@@ -1477,6 +1501,8 @@
 
     return ioctl( i_fd, SIOC_IO, &sctl_io );
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_SendKey2( i_fd, pi_agid, p_key );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_SEND_KEY, 12 );
     
@@ -1622,6 +1648,8 @@
     *p_mask = p_buffer[ 5 ];
     *p_scheme = p_buffer[ 6 ];
 
+#elif defined( SOLARIS_DOORS )
+    i_ret = door_ReportRPC( i_fd, p_type, p_mask, p_scheme );
 #elif defined( SOLARIS_USCSI )
     INIT_USCSI( GPCMD_REPORT_KEY, 8 );
     
@@ -1790,7 +1818,7 @@
 }
 #endif
 
-#if defined( SOLARIS_USCSI )
+#if defined( SOLARIS_USCSI ) && !defined( SOLARIS_DOORS )
 /*****************************************************************************
  * SolarisInitUSCSI: initialize a USCSICMD structure for the Solaris kernel
  *****************************************************************************
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/ioctl.h libdvdcss-1.2.3-jk/src/ioctl.h
--- libdvdcss-1.2.3-orig/src/ioctl.h	Sun Oct 13 01:06:49 2002
+++ libdvdcss-1.2.3-jk/src/ioctl.h	Sun Oct 20 23:51:45 2002
@@ -31,6 +31,7 @@
 int ioctl_InvalidateAgid    ( int, int * );
 int ioctl_SendChallenge     ( int, int *, u8 * );
 int ioctl_SendKey2          ( int, int *, u8 * );
+int ioctl_ReportRPC         ( int, int *, int *, int * );
 
 #define DVD_KEY_SIZE 5
 #define DVD_CHALLENGE_SIZE 10
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/soldoor_ioctl.c libdvdcss-1.2.3-jk/src/soldoor_ioctl.c
--- libdvdcss-1.2.3-orig/src/soldoor_ioctl.c	Thu Jan  1 01:00:00 1970
+++ libdvdcss-1.2.3-jk/src/soldoor_ioctl.c	Mon Oct 21 16:02:12 2002
@@ -0,0 +1,364 @@
+/*
+ * This file contains subroutines for the DVD ioctls, implemented as
+ * "door" procedure calls into a setuid-root server process
+ * ("dvdcss_door").
+ *
+ * Such a construct is needed, because Solaris 9 includes a security fix
+ * for the "vol" driver.  A process without root credentials is not
+ * allowed any more to send USCSICMD ioctl to the DVD-ROM device via the
+ * "/vol/dev/aliases/cdrom" vol device.
+ *
+ * The dvdcss_door server runs with the necessary root permission,  and
+ * we relay the open file descriptor and the arguments for the USCSICMD
+ * ioctl to the door server.  The door server performs the USCSICMD and
+ * sends back the result parameters.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <door.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include "common.h"
+#include "ioctl.h"
+#include "soldoor_server.h"
+#include "soldoor_ioctl.h"
+
+
+/* 
+ * Start the "door" server, and wait until it's ready to server requests.
+ */
+static int door_start_uscsi_server()
+{
+    pid_t pid, pid2;
+    int status;
+    int i;
+
+    pid = fork();
+    switch( pid ) {
+    case -1:
+	/* perror( "fork dvdcss_door server" ); */
+	return -1;
+    case 0:
+	/* do not pass random fds to the dvdcss_door daemon */
+	for(i = sysconf(_SC_OPEN_MAX); i >= 3; i--)
+	    close(i);
+	execl( EXEC_PREFIX "/libexec/dvdcss_door", "(dvdcss helper)", 0 );
+	perror( EXEC_PREFIX "/libexec/dvdcss_door" );
+	_exit( 1 );
+    default:
+	while( (pid2 = waitpid( pid, &status, 0 )) < 0 && errno == EINTR )
+	    ;
+	if( pid2 < 0 ) {
+	    /* perror( "waitpid" ); */
+	    return -1;
+	}
+
+	if( status )
+	    return -1;	/* exit status from dvdcss_door not OK */
+
+	/* exit status OK, dvdcss_door server should be up and runnnig */
+	return 0;
+    }
+}
+
+
+/*
+ * Pass an USCSICMD ioctl to the "door" server for execution with root
+ * credentials.
+ *
+ * The "door" USCSICMD server is automatically started, when we notice that
+ * it's not yet runing.
+ */
+static int door_uscsi_call( int i_fd,
+			    void *args, int args_size,
+			    void *rbuf, int rbuf_size )
+{
+    static int initialized;
+    static int door;
+    int i;
+    door_desc_t door_desc;
+    door_arg_t door_arg;
+
+    if( !initialized ) {
+	struct stat stb;
+	int start_server;
+	struct door_info di;
+
+	initialized = 1;
+
+	if( ( door = open( USCSI_DOOR, O_RDONLY ) ) < 0 
+	    || fstat( door, &stb ) < 0
+	    || !S_ISDOOR( stb.st_mode)
+	    || door_info( door, &di ) < 0
+	    || di.di_target == -1 )
+	{
+	    /* something is wrong with the "door" file entry, start server */
+	    start_server = 1;
+	} else {
+	    /* "door" file entry is there, test if server is up and running */
+	    start_server = door_call( door, NULL ) < 0;
+	}
+
+	if( start_server ) {
+	    if( door >= 0 )
+		close( door );
+
+	    door_start_uscsi_server();
+
+	    if( ( door = open( USCSI_DOOR, O_RDONLY ) ) < 0 
+		|| fstat( door, &stb ) < 0
+		|| !S_ISDOOR( stb.st_mode)
+		|| door_info( door, &di ) < 0
+		|| di.di_target == -1 ) {
+
+		/* still not working after a sever start? give up... */
+		if( door >= 0 )
+		    close( door );
+		door = -1;
+	    }
+	}
+
+    }
+
+    if( door < 0 )
+	return -1;
+
+    door_desc.d_attributes = DOOR_DESCRIPTOR;
+    door_desc.d_data.d_desc.d_descriptor = i_fd;
+
+    door_arg.data_ptr = args;
+    door_arg.data_size = args_size;
+    door_arg.desc_ptr = &door_desc;
+    door_arg.desc_num = 1;
+    door_arg.rbuf = rbuf;
+    door_arg.rsize = rbuf_size;
+    
+    /*
+     * According to the manual page, door_call() is not a restartable
+     * system call, so loop while we receive EINTR errors
+     */
+    while( ( i = door_call( door, &door_arg ) ) < 0 && errno == EINTR )
+	;
+    
+    if( i < 0 ) {
+	return -1;
+    }
+
+    return 0;
+}
+
+
+/*****************************************************************************
+ * door_ReadCopyright: check whether the disc is encrypted or not
+ *****************************************************************************/
+int door_ReadCopyright( int i_fd, int i_layer, int *pi_copyright )
+{
+    struct uscsi_ReadCopyright_args arg;
+    struct uscsi_ReadCopyright_res res;
+
+    arg.command = DOOR_USCSI_READCOPYRIGHT;
+    arg.layer = i_layer;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    *pi_copyright = res.copyright;
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_ReadDiscKey: get the disc key
+ *****************************************************************************/
+int door_ReadDiscKey( int i_fd, int *pi_agid, unsigned char *p_key )
+{
+    struct uscsi_ReadDiscKey_args arg;
+    struct uscsi_ReadDiscKey_res res;
+
+    arg.command = DOOR_USCSI_REPORT_DISCKEY;
+    arg.agid = *pi_agid;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    memcpy( p_key, res.key, DVD_DISCKEY_SIZE );
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * ioctl_ReadTitleKey: get the title key
+ *****************************************************************************/
+int door_ReadTitleKey(  int i_fd, int *pi_agid, int i_pos, unsigned char *p_key )
+{
+    struct uscsi_ReadTitleKey_args arg;
+    struct uscsi_ReadTitleKey_res res;
+
+    arg.command = DOOR_USCSI_REPORT_TITLEKEY;
+    arg.agid = *pi_agid;
+    arg.pos = i_pos;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    memcpy( p_key, res.key, DVD_KEY_SIZE );
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_ReportAgid: get AGID from the drive
+ *****************************************************************************/
+int door_ReportAgid( int i_fd, int *pi_agid )
+{
+    struct uscsi_ReportAgid_args arg;
+    struct uscsi_ReportAgid_res res;
+
+    arg.command = DOOR_USCSI_REPORT_AGID;
+    arg.agid = *pi_agid;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    *pi_agid = res.agid;
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_ReportChallenge: get challenge from the drive
+ *****************************************************************************/
+int door_ReportChallenge( int i_fd, int *pi_agid, unsigned char *p_challenge )
+{
+    struct uscsi_ReportChallenge_args arg;
+    struct uscsi_ReportChallenge_res res;
+
+    arg.command = DOOR_USCSI_REPORT_CHALLENGE;
+    arg.agid = *pi_agid;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    memcpy( p_challenge, res.challenge, DVD_CHALLENGE_SIZE );
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_ReportASF: get ASF from the drive
+ *****************************************************************************/
+int door_ReportASF( int i_fd, int *pi_remove_me, int *pi_asf )
+{
+    struct uscsi_ReportASF_args arg;
+    struct uscsi_ReportASF_res res;
+
+    arg.command = DOOR_USCSI_REPORT_ASF;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    *pi_asf = res.asf;
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_ReportKey1: get the first key from the drive
+ *****************************************************************************/
+int door_ReportKey1( int i_fd, int *pi_agid, unsigned char *p_key )
+{
+    struct uscsi_ReportKey1_args arg;
+    struct uscsi_ReportKey1_res res;
+
+    arg.command = DOOR_USCSI_REPORT_KEY1;
+    arg.agid = *pi_agid;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    memcpy( p_key, res.key, DVD_KEY_SIZE );
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_InvalidateAgid: invalidate the current AGID
+ *****************************************************************************/
+int door_InvalidateAgid( int i_fd, int *pi_agid )
+{
+    struct uscsi_InvalidateAgid_args arg;
+    struct uscsi_InvalidateAgid_res res;
+
+    arg.command = DOOR_USCSI_INVALIDATE_AGID;
+    arg.agid = *pi_agid;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    *pi_agid = res.agid;
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_SendChallenge: send challenge to the drive
+ *****************************************************************************/
+int door_SendChallenge( int i_fd, int *pi_agid, unsigned char *p_challenge )
+{
+    struct uscsi_SendChallenge_args arg;
+    struct uscsi_SendChallenge_res res;
+
+    arg.command = DOOR_USCSI_SEND_CHALLENGE;
+    arg.agid = *pi_agid;
+    memcpy( arg.challenge, p_challenge, DVD_CHALLENGE_SIZE );
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * ioctl_SendKey2: send the second key to the drive
+ *****************************************************************************/
+int door_SendKey2( int i_fd, int *pi_agid, unsigned char *p_key )
+{
+    struct uscsi_SendKey2_args arg;
+    struct uscsi_SendKey2_res res;
+
+    arg.command = DOOR_USCSI_SEND_KEY2;
+    arg.agid = *pi_agid;
+    memcpy( arg.key, p_key, DVD_KEY_SIZE );
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    return res.rval;
+}
+
+
+/*****************************************************************************
+ * door_ReportRPC: get RPC status for the drive
+ *****************************************************************************/
+int door_ReportRPC( int i_fd, int *p_type, int *p_mask, int *p_scheme )
+{
+    struct uscsi_ReportRPC_args arg;
+    struct uscsi_ReportRPC_res res;
+
+    arg.command = DOOR_USCSI_REPORT_RPC;
+    if( door_uscsi_call( i_fd,
+			 &arg, sizeof( arg ), &res, sizeof( res ) ) < 0 ) {
+	return -1;
+    }
+    *p_type = res.type;
+    *p_mask = res.mask;
+    *p_scheme = res.scheme;
+    return res.rval;
+}
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/soldoor_ioctl.h libdvdcss-1.2.3-jk/src/soldoor_ioctl.h
--- libdvdcss-1.2.3-orig/src/soldoor_ioctl.h	Thu Jan  1 01:00:00 1970
+++ libdvdcss-1.2.3-jk/src/soldoor_ioctl.h	Sun Oct 20 10:58:46 2002
@@ -0,0 +1,16 @@
+#ifndef	soldoor_ioctl_h
+#define soldoor_ioctl_h
+
+int door_ReadCopyright( int i_fd, int i_layer, int *pi_copyright );
+int door_ReadDiscKey( int i_fd, int *pi_agid, unsigned char *p_key );
+int door_ReadTitleKey(  int i_fd, int *pi_agid, int i_pos, unsigned char *p_key );
+int door_ReportAgid( int i_fd, int *pi_agid );
+int door_ReportChallenge( int i_fd, int *pi_agid, unsigned char *p_challenge );
+int door_ReportASF( int i_fd, int *pi_remove_me, int *pi_asf );
+int door_ReportKey1( int i_fd, int *pi_agid, unsigned char *p_key );
+int door_InvalidateAgid( int i_fd, int *pi_agid );
+int door_SendChallenge( int i_fd, int *pi_agid, unsigned char *p_challenge );
+int door_SendKey2( int i_fd, int *pi_agid, unsigned char *p_key );
+int door_ReportRPC( int i_fd, int *p_type, int *p_mask, int *p_scheme );
+
+#endif
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/soldoor_server.c libdvdcss-1.2.3-jk/src/soldoor_server.c
--- libdvdcss-1.2.3-orig/src/soldoor_server.c	Thu Jan  1 01:00:00 1970
+++ libdvdcss-1.2.3-jk/src/soldoor_server.c	Mon Oct 21 17:20:59 2002
@@ -0,0 +1,395 @@
+/*
+ * This file contains a tiny server running setuid-root.  It accepts
+ * solaris "door" calls (sent from soldoor_ioctl.c) for the DVD ioctls
+ * and executes them by sending USCSICMD ioctl to the dvd-rom's device
+ * driver.
+ */
+
+#define _REENTRANT
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <door.h>
+#include <sys/utsname.h>
+#include "common.h"
+#include "ioctl.h"
+#include "soldoor_server.h"
+
+
+/*
+ * Handle "door" calls
+ */
+static void
+uscsi_server_procedure (void *cookie,
+			char *argp, size_t arg_size,
+			door_desc_t *dp, uint_t n_desc)
+{
+    int command;
+    int fd = -1;
+
+    if (argp == DOOR_UNREF_DATA && dp == NULL) {
+	/*
+	 * only a single reference to the door remains (the one we have open
+	 * in this process).  Time to shutdown, as noone is able to talk to
+	 * us any more.
+	 */
+	exit(0);
+    }
+
+    if (argp == NULL && dp == NULL) {
+	/* A no-op test call, without arguments */
+	if (door_return(NULL, 0, NULL, 0) < 0)
+	    syslog(LOG_ERR, "door_return: %m");
+	return;
+    }
+
+    if (dp == NULL || n_desc != 1 || !(dp[0].d_attributes & DOOR_DESCRIPTOR)) {
+	syslog(LOG_INFO, "no fdesc arg");
+	goto error;
+    }
+    fd = dp[0].d_data.d_desc.d_descriptor;
+
+    if (argp == NULL || arg_size < sizeof(int)) {
+	syslog(LOG_ERR, "missing arg data");
+	goto error;
+    }
+    command = *(int *)argp;
+
+    switch (command) {
+    case DOOR_USCSI_READCOPYRIGHT:
+	if (arg_size == sizeof(struct uscsi_ReadCopyright_args)) {
+	    struct uscsi_ReadCopyright_args *ap = (void*) argp;
+	    struct uscsi_ReadCopyright_res res;
+
+	    res.rval = ioctl_ReadCopyright(fd, ap->layer, &res.copyright);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_REPORT_DISCKEY:
+	if (arg_size == sizeof(struct uscsi_ReadDiscKey_args)) {
+	    struct uscsi_ReadDiscKey_args *ap = (void*) argp;
+	    struct uscsi_ReadDiscKey_res res;
+
+	    res.rval = ioctl_ReadDiscKey(fd, &ap->agid, res.key);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_REPORT_TITLEKEY:
+	if (arg_size == sizeof(struct uscsi_ReadTitleKey_args)) {
+	    struct uscsi_ReadTitleKey_args *ap = (void*) argp;
+	    struct uscsi_ReadTitleKey_res res;
+
+	    res.rval = ioctl_ReadTitleKey(fd, &ap->agid, ap->pos, res.key);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_REPORT_AGID:
+	if (arg_size == sizeof(struct uscsi_ReportAgid_args)) {
+	    struct uscsi_ReportAgid_args *ap = (void*) argp;
+	    struct uscsi_ReportAgid_res res;
+
+	    res.rval = ioctl_ReportAgid(fd, &ap->agid);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    res.agid = ap->agid;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_REPORT_CHALLENGE:
+	if (arg_size == sizeof(struct uscsi_ReportChallenge_args)) {
+	    struct uscsi_ReportChallenge_args *ap = (void*) argp;
+	    struct uscsi_ReportChallenge_res res;
+
+	    res.rval = ioctl_ReportChallenge(fd, &ap->agid, res.challenge);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_REPORT_ASF:
+	if (arg_size == sizeof(struct uscsi_ReportASF_args)) {
+	    struct uscsi_ReportASF_args *ap = (void*) argp;
+	    struct uscsi_ReportASF_res res;
+
+	    res.rval = ioctl_ReportASF(fd, &ap->remove_me, &res.asf);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_REPORT_KEY1:
+	if (arg_size == sizeof(struct uscsi_ReportKey1_args)) {
+	    struct uscsi_ReportKey1_args *ap = (void*) argp;
+	    struct uscsi_ReportKey1_res res;
+
+	    res.rval = ioctl_ReportKey1(fd, &ap->agid, res.key);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_INVALIDATE_AGID:
+	if (arg_size == sizeof(struct uscsi_InvalidateAgid_args)) {
+	    struct uscsi_InvalidateAgid_args *ap = (void*) argp;
+	    struct uscsi_InvalidateAgid_res res;
+
+	    res.rval = ioctl_InvalidateAgid(fd, &ap->agid);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    res.agid = ap->agid;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_SEND_CHALLENGE:
+	if (arg_size == sizeof(struct uscsi_SendChallenge_args)) {
+	    struct uscsi_SendChallenge_args *ap = (void*) argp;
+	    struct uscsi_SendChallenge_res res;
+
+	    res.rval = ioctl_SendChallenge(fd, &ap->agid, ap->challenge);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_SEND_KEY2:
+	if (arg_size == sizeof(struct uscsi_SendKey2_args)) {
+	    struct uscsi_SendKey2_args *ap = (void*) argp;
+	    struct uscsi_SendKey2_res res;
+
+	    res.rval = ioctl_SendKey2(fd, &ap->agid, ap->key);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    case DOOR_USCSI_REPORT_RPC:
+	if (arg_size == sizeof(struct uscsi_ReportRPC_args)) {
+	    struct uscsi_ReportRPC_res res;
+
+	    res.rval = ioctl_ReportRPC(fd, &res.type, &res.mask, &res.scheme);
+	    if (res.rval < 0)
+		res.sys_errno = errno;
+	    close(fd);
+	    if (door_return((void*) &res, sizeof(res), NULL, 0) < 0)
+		syslog(LOG_ERR, "door_return: %m");
+	} else {
+	    syslog(LOG_ERR, "invalid argument size");
+	    goto error;
+	}
+	break;
+
+    default:
+	syslog(LOG_INFO, "unknown command %d", command);
+	goto error;
+    }
+    return;
+
+error:
+    if (fd >= 0)
+	close(fd);
+    if (door_return(NULL, 0, NULL, 0) < 0)
+	syslog(LOG_ERR, "door_return: %m");
+}
+
+
+static void
+usage()
+{
+    fprintf(stderr, "usage: dvdcss_door\n");
+    exit(1);
+}
+
+
+int
+main(int argc, char **argv)
+{
+    int debug = 0;
+    int c;
+    int door;
+    int fd;
+    int sync_pipe[2];
+    char ch;
+
+    while ((c = getopt(argc, argv, "d")) != -1) {
+	switch (c) {
+	case 'd':
+	    debug = 1;
+	    break;
+	case '?':
+	default:
+	    usage();
+	}
+    }
+
+    openlog("dvdcss_door", LOG_PID, LOG_DAEMON);
+
+    /* 
+     * Not running as root?
+     * That's the whole purpose of the this deamon, to be able to send
+     * USCSICMDs to the DVD-ROM device with root credentials.  Which is
+     * required for Solaris 9.
+     */
+    if (geteuid()) {
+	struct utsname unm;
+	int sunos5_9;
+	int major, minor;
+	
+	/* are we running on Solaris 9 (aka SunOS 5.9) or later? */
+	uname(&unm);
+	sunos5_9 = sscanf(unm.release, "%d.%d", &major, &minor) == 2
+	    && (major > 5 || (major == 5 && minor >= 9));
+
+	syslog(sunos5_9 ? LOG_ERR : LOG_INFO,
+	       "server must run with root credentials");
+    }
+
+    setgid(0);
+    setuid(0);
+
+    if (!debug) {
+	chdir("/");
+
+	close(0); 
+	close(1);
+	close(2);
+	if (open("/dev/null", O_RDWR) != 0 || dup(0) != 1 || dup(0) != 2)
+	    exit(2);
+
+	pipe(sync_pipe);
+
+	/* we're a daemon process, automatically run in background */
+	switch (fork()) {
+	case -1:
+	    syslog(LOG_ERR,
+		   "unable to run as background daemon, fork failed: %m");
+	    exit(1);
+	default:
+	    /*
+	     * real server continues in the child process,
+	     * parent exits when server is ready
+	     */
+	    close(sync_pipe[1]);
+	    ch = 0;
+	    read(sync_pipe[0], &ch, 1);
+	    exit(ch == '*' ? 0 : 3);
+	case 0:
+	    /* child: continues to run as server */
+	    close(sync_pipe[0]);
+	    break;
+	}
+
+	setsid();
+    }
+
+    /* create the "door" */
+    door = door_create(uscsi_server_procedure, NULL, DOOR_UNREF);
+    if (door < 0) {
+	syslog(LOG_ERR, "door_create failed: %m");
+	exit(1);
+    }
+
+    /* an (empty) file is needed, so we can attach the "door" to it */
+    fd = open(USCSI_DOOR, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+    if (fd < 0) {
+	syslog(LOG_ERR, "cannot create " USCSI_DOOR " file: %m");
+	exit(1);
+    }
+    close(fd);
+    
+    /*
+     * clean up any old "door" attached to the file, just in case it was
+     * left lying around in case of a "door" server crash.
+     *
+     * And attach the "door" to the empty file.
+     */
+    fdetach(USCSI_DOOR);
+    if (fattach(door, USCSI_DOOR) < 0) {
+	syslog(LOG_ERR, "fattach " USCSI_DOOR " door: %m");
+	exit(1);
+    }
+
+    if (!debug) {
+	/*
+	 * we're ready to serve requests, notify parent by sending a
+	 * special token through the sync_pipe.
+	 */
+	write(sync_pipe[1], "*", 1);
+	close(sync_pipe[1]);
+    }
+
+    while (1)
+	pause();
+
+    exit(0);
+}
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/soldoor_server.h libdvdcss-1.2.3-jk/src/soldoor_server.h
--- libdvdcss-1.2.3-orig/src/soldoor_server.h	Thu Jan  1 01:00:00 1970
+++ libdvdcss-1.2.3-jk/src/soldoor_server.h	Mon Oct 21 11:23:20 2002
@@ -0,0 +1,175 @@
+
+/*
+ * USCSI_DOOR: a fixed path name in the filesystem, clients can use it
+ * to connect to the DVD ioctl uscsi server.
+ *
+ * /var/run does not exist on old solaris releases, so use /tmp for now
+ * #define USCSI_DOOR "/var/run/dvdcss_door"
+ */
+#define	USCSI_DOOR "/tmp/.dvdcss_door"
+
+enum door_uscsi_cmd {
+    DOOR_USCSI_READCOPYRIGHT = 1,
+    DOOR_USCSI_REPORT_DISCKEY,
+    DOOR_USCSI_REPORT_TITLEKEY,
+    DOOR_USCSI_REPORT_AGID,
+    DOOR_USCSI_REPORT_CHALLENGE,
+    DOOR_USCSI_REPORT_ASF,
+    DOOR_USCSI_REPORT_KEY1,
+    DOOR_USCSI_INVALIDATE_AGID,
+    DOOR_USCSI_SEND_CHALLENGE,
+    DOOR_USCSI_SEND_KEY2,
+    DOOR_USCSI_REPORT_RPC,
+};
+
+
+/*
+ * XXX_args and XXX_res structures, one for each request. XXX_args
+ * contains the request's input parameters, and XXX_res the result
+ * values.
+ */
+
+/* DOOR_USCSI_READCOPYRIGHT */
+struct uscsi_ReadCopyright_args {
+    enum door_uscsi_cmd command;
+    int layer;
+};
+
+struct uscsi_ReadCopyright_res {
+    int rval;
+    int sys_errno;
+    int copyright;
+};
+
+
+/* DOOR_USCSI_REPORT_DISCKEY */
+struct uscsi_ReadDiscKey_args {
+    enum door_uscsi_cmd command;
+    int agid;
+};
+
+struct uscsi_ReadDiscKey_res {
+    int rval;
+    int sys_errno;
+    unsigned char key[DVD_DISCKEY_SIZE];
+};
+
+
+/* DOOR_USCSI_REPORT_TITLEKEY */
+struct uscsi_ReadTitleKey_args {
+    enum door_uscsi_cmd command;
+    int agid;
+    int pos;
+};
+
+struct uscsi_ReadTitleKey_res {
+    int rval;
+    int sys_errno;
+    unsigned char key[DVD_KEY_SIZE];
+};
+
+
+/* DOOR_USCSI_REPORT_AGID */
+struct uscsi_ReportAgid_args {
+    enum door_uscsi_cmd command;
+    int agid;
+};
+
+struct uscsi_ReportAgid_res {
+    int rval;
+    int sys_errno;
+    int agid;
+};
+
+
+/* DOOR_USCSI_REPORT_CHALLENGE */
+struct uscsi_ReportChallenge_args {
+    enum door_uscsi_cmd command;
+    int agid;
+};
+
+struct uscsi_ReportChallenge_res {
+    int rval;
+    int sys_errno;
+    unsigned char challenge[DVD_CHALLENGE_SIZE];
+};
+
+
+/* DOOR_USCSI_REPORT_ASF */
+struct uscsi_ReportASF_args {
+    enum door_uscsi_cmd command;
+    int remove_me;
+};
+
+struct uscsi_ReportASF_res {
+    int rval;
+    int sys_errno;
+    int asf;
+};
+
+
+/* DOOR_USCSI_REPORT_KEY1 */
+struct uscsi_ReportKey1_args {
+    enum door_uscsi_cmd command;
+    int agid;
+};
+
+struct uscsi_ReportKey1_res {
+    int rval;
+    int sys_errno;
+    unsigned char key[DVD_KEY_SIZE];
+};
+
+
+/* DOOR_USCSI_INVALIDATE_AGID */
+struct uscsi_InvalidateAgid_args {
+    enum door_uscsi_cmd command;
+    int agid;
+};
+
+struct uscsi_InvalidateAgid_res {
+    int rval;
+    int sys_errno;
+    int agid;
+};
+
+
+/* DOOR_USCSI_SEND_CHALLENGE */
+struct uscsi_SendChallenge_args {
+    enum door_uscsi_cmd command;
+    int agid;
+    unsigned char challenge[DVD_CHALLENGE_SIZE];
+};
+
+struct uscsi_SendChallenge_res {
+    int rval;
+    int sys_errno;
+};
+
+
+/* DOOR_USCSI_SEND_KEY2 */
+struct uscsi_SendKey2_args {
+    enum door_uscsi_cmd command;
+    int agid;
+    unsigned char key[DVD_KEY_SIZE];
+};
+
+struct uscsi_SendKey2_res {
+    int rval;
+    int sys_errno;
+};
+
+
+/* DOOR_USCSI_REPORT_RPC */
+struct uscsi_ReportRPC_args {
+    enum door_uscsi_cmd command;
+    int agid;
+};
+
+struct uscsi_ReportRPC_res {
+    int rval;
+    int sys_errno;
+    int type;
+    int mask;
+    int scheme;
+};
diff -ru -N -x *.d -x mkinstalldirs -x missing -x ltmain.sh -x install-sh -x depcomp -x config.h.in -x configure -x aclocal.m4 -x Makefile.in libdvdcss-1.2.3-orig/src/soldoor_uscsi.c libdvdcss-1.2.3-jk/src/soldoor_uscsi.c
--- libdvdcss-1.2.3-orig/src/soldoor_uscsi.c	Thu Jan  1 01:00:00 1970
+++ libdvdcss-1.2.3-jk/src/soldoor_uscsi.c	Sun Oct 20 19:11:03 2002
@@ -0,0 +1,17 @@
+/*
+ * This file contains the real implementation of the Solaris DVD ioctls,
+ * implemented as USCSICMD ioctls passing SCSI commands from userland to
+ * the DVD-ROM driver.
+ *
+ * On Solaris 9 the USCSICMD ioctls will only work when the process using
+ * these subroutines has root credentials.
+ */
+
+#define _REENTRANT
+
+/*
+ * pull in the real solaris uscsi implementation of the DVD ioctls from the
+ * ioctl.c file, using some preprocessor magic
+ */
+#define SOLDOOR_COMPILE_USCSI 1
+#include "ioctl.c"


More information about the libdvdcss-devel mailing list