Changeset 3276

Show
Ignore:
Timestamp:
01/08/08 00:03:01 (16 years ago)
Author:
jerome
Message:

Doesn't drop and regain priviledges anymore : no added security since we could regain them (we needed to regain them for PAM and some end user scripts). This is also more consistent.
Removed SGTERM handling stuff in cupspykota : now only SIGINT can be used.
Now outputs an error message when printing (but doesn't fail) if CUPS is
not v1.3.4 or higher : we need 1.3.4 or higher because it fixes some
problematic charset handling bugs (by only accepting ascii and utf-8,
but this is a different story...)
Now ensures only the supported exit codes are returned by cupspykota :
we used to exit -1 in some cases (see man backend for details).

Location:
pykota/trunk
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/bin/cupspykota

    r3275 r3276  
    7272        signal.signal(signal.SIGTERM, signal.SIG_IGN) 
    7373        signal.signal(signal.SIGPIPE, signal.SIG_IGN) 
     74        self.oldSigIntHandler = signal.signal(signal.SIGINT, signal.SIG_IGN) 
    7475        self.MyName = "PyKota" 
    7576        self.myname = "cupspykota" 
     
    8485        if not self.config.isAdmin : 
    8586            from pykota import config 
    86             username = self.originalUserName 
     87            username = self.effectiveUserName 
    8788            raise config.PyKotaConfigError, _("User %(username)s is not allowed to read ~pykota/pykotadmin.conf, you must check the permissions.") % locals() 
    88         self.gotSigTerm = 0 
    89         self.disableSigInt() 
    90         self.installSigTermHandler() 
    91          
    92     def sigtermHandler(self, signum, frame) : 
    93         """Sets an attribute whenever SIGTERM is received.""" 
    94         self.gotSigTerm = 1 
    95         self.printInfo(_("SIGTERM received, job %s cancelled.") % self.Ticket.JobId) 
    96         os.environ["PYKOTASTATUS"] = "CANCELLED" 
    97          
    98     def deinstallSigTermHandler(self) :            
    99         """Deinstalls the SIGTERM handler.""" 
    100         self.logdebug("Deinstalling SIGTERM handler...") 
    101         signal.signal(signal.SIGTERM, signal.SIG_IGN) 
    102         self.logdebug("SIGTERM handler deinstalled.") 
    103          
    104     def installSigTermHandler(self) :            
    105         """Installs the SIGTERM handler.""" 
    106         self.logdebug("Installing SIGTERM handler...") 
    107         signal.signal(signal.SIGTERM, self.sigtermHandler) 
    108         self.logdebug("SIGTERM handler installed.") 
    109          
    110     def disableSigInt(self) :     
    111         """Disables the SIGINT signal (which raises KeyboardInterrupt).""" 
    112         self.logdebug("Disabling SIGINT...") 
    113         self.oldSigIntHandler = signal.signal(signal.SIGINT, signal.SIG_IGN) 
    114         self.logdebug("SIGINT disabled.") 
    11589         
    11690    def enableSigInt(self) :     
    11791        """Enables the SIGINT signal (which raises KeyboardInterrupt).""" 
    118         self.logdebug("Enabling SIGINT...") 
    11992        signal.signal(signal.SIGINT, self.oldSigIntHandler) 
    120         self.logdebug("SIGINT enabled.") 
    12193         
    12294    def waitForLock(self) :     
     
    153125           Executes each existing backend in turn in device enumeration mode. 
    154126           Returns the list of available backends. 
     127            
     128           Unfortunately this method can't output any debug information 
     129           to stdout or stderr, else CUPS considers that the device is 
     130           not available. 
    155131        """ 
    156         # Unfortunately this method can't output any debug information 
    157         # to stdout or stderr, else CUPS considers that the device is 
    158         # not available. 
    159132        available = [] 
    160133        (directory, myname) = os.path.split(sys.argv[0]) 
     
    226199        return available 
    227200                         
     201    def checkCUPSVersion(self) :                     
     202        """Checks if CUPS is not v1.3.4 or higher.""" 
     203        fullversion = os.environ.get("SOFTWARE", "") 
     204        if fullversion.startswith("CUPS/") : 
     205            vnum = fullversion.split("/")[1] 
     206            try : 
     207               (major, minor, release) = [int(p) for p in vnum.split(".")] 
     208            except ValueError :    
     209                pass 
     210            else :     
     211                return (major > 1) \ 
     212                        or ((major == 1) and (minor > 3)) \ 
     213                        or ((major == 1) and (minor == 3) and (release >= 4)) 
     214        return False 
     215         
    228216    def initBackendParameters(self) :     
    229217        """Initializes the backend's attributes.""" 
     
    233221        # the environment before launching the real backend  
    234222        self.logdebug("Initializing backend...") 
     223         
     224        if not self.checkCUPSVersion() : 
     225            self.printInfo("BEWARE : CUPS is too old. You must use CUPS v1.3.4 or higher.", "error") 
    235226         
    236227        self.PrinterName = os.environ.get("PRINTER", "") 
     
    410401            self.logdebug("Launching subprocess [%s] to overwrite the job ticket." \ 
    411402                                     % jobticketcommand) 
    412             self.regainPriv()                          
    413403            inputfile = os.popen(jobticketcommand, "r") 
    414404            try : 
     
    433423                self.logdebug("IOError while reading subprocess' output : %s" % msg) 
    434424            inputfile.close()     
    435             self.dropPriv() 
    436425             
    437426            # now overwrite the job's ticket if new data was supplied 
     
    458447        outfile = open(self.DataFile, "wb")     
    459448        if self.Ticket.FileName is not None : 
    460             self.regainPriv() 
    461449            infile = open(self.Ticket.FileName, "rb") 
    462450            self.logdebug("Reading input datas from %s" % self.Ticket.FileName) 
     
    481469        if mustclose :     
    482470            infile.close() 
    483             self.dropPriv() 
    484471             
    485472        outfile.close() 
     
    494481        """Cleans up the place.""" 
    495482        self.logdebug("Cleaning up...") 
    496         self.regainPriv() 
    497         self.deinstallSigTermHandler() 
    498483        if (self.DataFile is not None) and os.path.exists(self.DataFile) : 
    499484            try : 
     
    933918            if mailto == "EXTERNAL" : 
    934919                # TODO : clean this again 
    935                 self.regainPriv() 
    936920                self.externalMailTo(arguments, self.Action, self.User, self.Printer, self.Reason) 
    937                 self.dropPriv() 
    938921            else :     
    939922                # TODO : clean this again 
     
    11141097        else : 
    11151098            self.printInfo(_("Job accounting begins.")) 
    1116             self.deinstallSigTermHandler() 
    11171099            self.accounter.beginJob(self.Printer) 
    1118             self.installSigTermHandler() 
    11191100         
    11201101        # handle starting banner pages with accounting 
    11211102        if (self.Action != "CANCEL") and accountbanner in ["STARTING", "BOTH"] : 
    1122             if not self.gotSigTerm : 
    1123                 self.handleBanner("starting", 1) 
    1124          
    1125         # pass the job's data to the real backend     
    1126         if (not self.gotSigTerm) and (self.Action in ["ALLOW", "WARN"]) : 
     1103            self.handleBanner("starting", 1) 
     1104         
     1105        # pass the job's data to the real backend if needed    
     1106        if self.Action in ("ALLOW", "WARN") : 
    11271107            retcode = self.printJobDatas() 
    11281108        else :         
     
    11341114        # handle ending banner pages with accounting 
    11351115        if (self.Action != "CANCEL") and accountbanner in ["ENDING", "BOTH"] : 
    1136             if not self.gotSigTerm : 
    1137                 self.handleBanner("ending", 1) 
     1116            self.handleBanner("ending", 1) 
    11381117         
    11391118        # stops accounting 
     
    11431122            self.printInfo(_("Job cancelled, no accounting has been done.")) 
    11441123        else : 
    1145             self.deinstallSigTermHandler() 
    11461124            self.accounter.endJob(self.Printer) 
    1147             self.installSigTermHandler() 
    11481125            self.printInfo(_("Job accounting ends.")) 
    11491126         
     
    12841261         
    12851262        self.logdebug("Starting original backend %s with args %s" % (originalbackend, " ".join(['"%s"' % a for a in arguments]))) 
    1286         self.regainPriv()     
    12871263        pid = os.fork() 
    12881264        self.logdebug("Forked !") 
     
    12981274            self.logdebug("We shouldn't be there !!!")     
    12991275            os._exit(-1) 
    1300         self.dropPriv()     
    13011276         
    13021277        self.logdebug("Waiting for original backend to exit...")     
    1303         killed = 0 
     1278        killed = False 
    13041279        status = -1 
    13051280        while status == -1 : 
     
    13071282                status = os.waitpid(pid, 0)[1] 
    13081283            except OSError, (err, msg) : 
    1309                 if (err == 4) and self.gotSigTerm : 
    1310                     os.kill(pid, signal.SIGTERM) 
    1311                     killed = 1 
     1284                if err == 4 : 
     1285                    killed = True 
    13121286                     
    13131287        if os.WIFEXITED(status) : 
     
    13251299            self.Reason = "CUPS backend %s died abnormally." % originalbackend 
    13261300            self.printInfo(self.Reason, "error") 
    1327             return -1 
     1301            return 1 
    13281302        else : 
    13291303            self.Reason = "CUPS backend %s was killed." % originalbackend 
  • pykota/trunk/bin/pkinvoice

    r3275 r3276  
    198198                                        pageCompression=1) 
    199199         
    200         c.setAuthor(self.originalUserName) 
     200        c.setAuthor(self.effectiveUserName) 
    201201        c.setTitle("PyKota invoices") 
    202202        c.setSubject("Invoices generated with PyKota") 
  • pykota/trunk/bin/pknotify

    r3275 r3276  
    180180            retcode = False 
    181181            self.password = password 
    182             self.regainPriv() 
    183182            auth = PAM.pam() 
    184183            auth.start("passwd") 
     
    195194                self.logdebug(_("Password correct for user %s") % username) 
    196195                retcode = True 
    197             self.dropPriv()     
    198196            return retcode 
    199197             
  • pykota/trunk/bin/pkprinters

    r3275 r3276  
    171171        if uri and (not self.isPrinterCaptured(deviceuri=uri)) : 
    172172             newuri = "cupspykota://%s" % uri 
    173              self.regainPriv() # to avoid having to enter password. 
    174173             os.system('lpadmin -p "%s" -v "%s"' % (printer.Name, newuri)) 
    175174             self.logdebug("Printer %s rerouted to %s" % (printer.Name, newuri)) 
    176              self.dropPriv() 
    177175              
    178176    def deroutePrinterFromPyKota(self, printer) :     
     
    183181             if newuri.startswith("//") : 
    184182                 newuri = newuri[2:] 
    185              self.regainPriv() # to avoid having to enter password. 
    186183             os.system('lpadmin -p "%s" -v "%s"' % (printer.Name, newuri)) 
    187184             self.logdebug("Printer %s rerouted to %s" % (printer.Name, newuri)) 
    188              self.dropPriv()     
    189185                                      
    190186    def main(self, names, options) : 
  • pykota/trunk/bin/pkrefund

    r3275 r3276  
    209209                                        pageCompression=1) 
    210210         
    211         c.setAuthor(self.originalUserName) 
     211        c.setAuthor(self.effectiveUserName) 
    212212        c.setTitle("PyKota print job refunding receipts") 
    213213        c.setSubject("Print job refunding receipts generated with PyKota") 
  • pykota/trunk/pykota/storage.py

    r3275 r3276  
    489489                        % (self.JobSize, 
    490490                           self.JobPrice, 
    491                            self.parent.tool.originalUserName, 
     491                           self.parent.tool.effectiveUserName, 
    492492                           os.getlogin(), 
    493493                           str(DateTime.now())[:19]) 
  • pykota/trunk/pykota/tool.py

    r3275 r3276  
    132132        self.logger = logger.openLogger("stderr") 
    133133         
    134         # did we drop priviledges ? 
    135         self.privdropped = 0 
    136          
    137134        # locale stuff 
    138135        try : 
     
    167164        # pykota specific stuff 
    168165        self.documentation = doc 
     166         
     167        # Extract the effective username 
     168        uid = os.geteuid() 
     169        try : 
     170            self.effectiveUserName = pwd.getpwuid(uid)[0] 
     171        except (KeyError, IndexError), msg :     
     172            self.printInfo(_("Strange problem with uid(%s) : %s") % (uid, msg), "warn") 
     173            self.effectiveUserName = os.getlogin() 
    169174         
    170175    def deferredInit(self) :         
     
    190195        self.logger = logger.openLogger(self.config.getLoggingBackend()) 
    191196             
    192         # now drop priviledge if possible 
    193         self.dropPriv()     
    194          
    195197        # We NEED this here, even when not in an accounting filter/backend     
    196198        self.softwareJobSize = 0 
     
    208210        arguments = " ".join(['"%s"' % arg for arg in sys.argv]) 
    209211        self.logdebug("Command line arguments : %s" % arguments) 
    210          
    211     def dropPriv(self) :     
    212         """Drops priviledges.""" 
    213         uid = os.geteuid() 
    214         try : 
    215             self.originalUserName = pwd.getpwuid(uid)[0] 
    216         except (KeyError, IndexError), msg :     
    217             self.printInfo(_("Strange problem with uid(%s) : %s") % (uid, msg), "warn") 
    218             self.originalUserName = None 
    219         else : 
    220             if uid : 
    221                 self.logdebug(_("Running as user '%s'.") % self.originalUserName) 
    222             else : 
    223                 if self.pykotauser is None : 
    224                     self.logdebug(_("No user named 'pykota'. Not dropping priviledges.")) 
    225                 else :     
    226                     try : 
    227                         os.setegid(self.pykotauser[3]) 
    228                         os.seteuid(self.pykotauser[2]) 
    229                     except OSError, msg :     
    230                         self.printInfo(_("Impossible to drop priviledges : %s") % msg, "warn") 
    231                     else :     
    232                         self.logdebug(_("Priviledges dropped. Now running as user 'pykota'.")) 
    233                         self.privdropped = 1 
    234              
    235     def regainPriv(self) :     
    236         """Drops priviledges.""" 
    237         if self.privdropped : 
    238             try : 
    239                 os.seteuid(0) 
    240                 os.setegid(0) 
    241             except OSError, msg :     
    242                 self.printInfo(_("Impossible to regain priviledges : %s") % msg, "warn") 
    243             else :     
    244                 self.logdebug(_("Regained priviledges. Now running as root.")) 
    245                 self.privdropped = 0 
    246212         
    247213    def UTF8ToUserCharset(self, text) :