Not doing your best can hurt...
I'm creating an installer right now. Trouble is, Apple's PackageMaker is kinda buggy, insisting on updating existing copies it finds on another partition instead of installing it in the absolute path I need it to be in. Workarounds suggested by various people fail for various other reasons, like PackageMaker simply forgetting I ticked off a particular setting and checking it again... So I went looking for third-party package makers. One of them seemed OK, then I ran the installer, which told me it had to restart the Mac after installation.
That made me suspicious, so I brought up the Installer's list of files using the "Show Files" menu item and had a look at it. It actually installed a system Startup Item in /Library/Startup Items/. That was the point I decided to roll my own installer.
Why? Well, for one, an application that is simply there to create installer files shouldn't need a startup item that runs when I boot up my Mac, even if I never want to create an installer this session. So, there are two options here:
- This application isn't doing what it says it does, but rather something malicious.
- The person who created this application didn't know better.
The first case is luckily still pretty rare these days, and I'm not paranoid enough to believe it actually applies in this case, which leaves us with case 2. And now we finally come around to my point: If I'm intending to use a program to create a ship-worthy product, why would I use an installer-maker whose creator thinks to make installers he needs to run code every time my Mac starts up?
We've arrived again at perceived quality. Every decision I make in the design of my application has the potential of not only damaging its actual quality, but also its perceived quality. If I, as the developer do things in a weird way in this one spot, what are the chances that the rest of the app is just as weird? If I, as the programmer, show bad judgement by turning what should be a simple app for editing a special kind of document into a system service that needs to be constantly running, then how much other similar mistakes did I make?
So, if there's a way to do things in a weird, but easy way, or in a harder but more sensible way, today I learned that I have another reason to do things the sensible way, even if it takes a bit longer. It's just not worth freaking out your users to save an hour ;-)
|Uli Kusterer replies: ★|
I didn't want to mention it, but the third-party installer maker I was talking about *was* Iceberg. Iceberg is the installer maker that insists on installing a system startup item and restarting your Mac.
As the developer of this "malicious" application, maybe I can shed some lights on the EVIL startup item.
- This application runs on Mac OS X 10.2 and later. launchd is 10.4 and later only and a launchd user agent works correctly on 10.5 and later. So this explains why it's a startup item and not a launchd thing.
- Now why does it need to run as root? When you create a package, most of the time it's to install stuff in /Applications, /Library or /System/Library/Extensions. For a package to be correct from a file permissions and ownership point of view, at some point you must have your package creation tool running as root. So you can either do this by requesting the user to enter its admin password every time you build your package (the (old?) PackageMaker way) (or you can try to be a bit more clever and keep the authorization credential as long as the program runs), you can use a suid tool (the (old?) Disk Utility way) or you can use a background task running as root. (*)
The second case is called a security flaw, the first case can become a security flaw if you do not check that the secondary tool you use is really the real original tool and add some handshake check code. The 3rd case is the most secure and simple IMHO because all the components are in /Library/StartupItems folder which is root:wheel protected on the most recent Mac OS X versions (you will notice there is no such default folder for launchd items).
- Regarding the launch at boot time, I won't deny I received a lot of feedback from users concerned about this. (3 users in 4 years). Usually, I tell them to check the CPU usage of this background tool when no packages are being built. IIRC, the value is between -0.0000000000000000001 and 0.00000000000000000000000001. If you're able to get it used more than 10 minutes of the CPU time while letting your Mac run 24/7 during a year and without building a package, I will offer you a moose puppet. From what you could see from the source code (which is available), this background task does nothing but wait for a CFMessagePort message.
For the record I had a look some years ago at launchd and dynamically launching the background tool this way, so that no nanoseconds are wasted on a Mac where Gigaflops of CPU and a lot of Watts are used to throb the OK button. At this time, the launchd trigger mechanism did not support CFMessagePort but only MachPort. So I did not investigate this further. AFAIK, CFMessagePort are still not supported...
So, considering this application runs on 10.2 or later (and when something works I don't try to improve it without a really vital reason (such as Startup Items not working anymore in 10.x where x>=6)), the real dilemma regarding this implementation was:
- do I want to be a pain in the ass of users and ask them their admin password almost every time they want to build a package?
- do I sacrifice a few seconds of startup time to avoid providing some vaseline with the application?
This being a free application, I could not afford huge bills of vaseline. So I ended up with option 2.
Should there be a version of the application running only on Tiger and later, I would use launchd and avoid a system restart. I still have one or two neurons running.
* 4th solution could be to use the root-escalation tool available for Mac OS X 10.3 (or is it 4?) and earlier which allows anyone to become root. Now, that would be weird and malicious at the same time!
|Uli Kusterer replies: ★|
Stephane, As I wrote, I don't think it's malicious (which is why I originally didn't mention the name 'Iceberg'). But programs have bugs, and installing a startup item or other background daemon when all you need is run root code occasionally means that your code gets run in situations where the user doesn't even know your code may be involved. In a worst-case-scenario, your app may cause a crash at startup and make a Mac unbootable.
OTOH, a suid-tool may be a bit more involved to get right, but it will only be run when someone actually calls upon it. So the worst-case-crash could at worst happen when the user actually launches your app.
Also, all of the security considerations for a suid tool also apply for your daemon: You have to make sure only root/admin has write permissions, you have to validate whoever actually calls it in some way... I don't really see what the daemon would bring you here.
"In a worst-case-scenario, your app may cause a crash at startup and make a Mac unbootable"
It looks like that you're confused when it comes to StartupItems. StartupItems != Kernel Extension. Would the background task crashed (never got a report about this in 4 years), it would have absolutely NO impact on the startup procedure. It's just a user land application. A StartupItem can be used to launch a background task. Apple uses them in Mac OS X 10.0, 10.1, 10.2, 10.3 to launch system services (such as Apache, AppleShare, etc.). I've seen my share of background tasks crashing at startup time and as long as no kernel extensions were involved, it had never prevented me from starting up a Mac.
When it comes to file permissions, root/admin is worse than root/wheel. I can replace a root/admin file from the default user account without having to enter my admin password. I can not do this for an item inside /Library/StartupItems/.
- does this solution eat CPU time for nothing? No. The throbbing OK button in the Authentication dialog eats more CPU every time you have to enter your password.
- can this solution prevent the OS from starting up? No.
- has the background task (the one launched by the StartupItem) ever crashed in deployment? AFAIK, never.
- is this solution secure? As good as possible.
- is this solution more convenient for users? I think so.
|Uli Kusterer replies: ★|
Just to clarify: That was either root or admin, not root:admin as a user:group pair. Also, I said *worst case*. As soon as you run as root or another powerful user, there are crashes that can take down the whole Mac. The easiest example is excessive resource usage.