Home

Advertisement

Previous Entry | Next Entry


Using associative containers (be it Perl hashes, C++ std::maps or YCP maps) may be certainly fun. Inserting/retrieving/changing values is efficient and easy, once we know the key. However, elements of every associative container are ordered, one way or the other, and one cannot really retrieve them all back in the order of insertion. Though it is certainly beneficial feature sometimes, it makes associative containers unusable in other cases, especially in those where the order matters.
Recently I had particularly excellent time with STL C++'s maps while fixing bug #439088 (a.k.a. "How to make hierarchical structure, which I cannot visualize, flat and keep the ordering of elements at the same time") ;-) :-D but that's not what I am going to write about. Now listen to the story of yast2-sudo:

Juggling with sudo rules


openSUSE 10.2 was the first release that came with user-friendly way of creating/editing sudo rules in YaST. If you ever wondered what are these for, they enable 'normal' (non-root) users to run specified commands as root (or other user), without actually knowing his password. The rules are stored in /etc/sudoers file and ... yes, right, the order really matters there. What does it mean? As the user invokes sudo, sudoers file is searched for matching rules. If multiple rules happen to match our user, the last one of those is taken into account. Which may not, unfortunately, be also the best one ;-)

Consider the following example: on your machine, you want to enable Tux the Penguin (user tux) to install additional cool software from openSUSE repositories by running YaST software (sw_single) module, but you don't want disclose root password to him. Easy as that, you will add a new rule to sudoers file that will enable Tux to run yast sw_single as the user in root group (and we won't ask him for password, too) as follows:


# don't try this at home, it will not do what you want :)
tux  ALL = (%root) NOPASSWD: /sbin/yast sw_single
ALL  ALL = (ALL) ALL  

Now, will this really do the job? As mentioned above, out of multiple matching rules, the last one is applied. Tux matches the first rule, but also the second one, which will be unfortunately used in this case ('ALL' is a catch-all alias for any user - the rule basically says that everybody can do anything, but needs to enter the target user's password in order to do so). So this does not really help us. We want to do something like this:


# this is the right way
ALL     ALL = (ALL) ALL  
tux  ALL = (%root) NOPASSWD: /sbin/yast sw_single

In other words, we want to place our rule to /etc/sudoers in such a way, that it will be the last matching one. And, last but not least, it would be also good if a configuration tool respected the order and didn't re-shuffle the rules whenever it sees fit, now wouldn't it?

Being an inexperienced greenhorn


yast2-sudo was the module that made me jump headlong into YaST development and, at the end, made a real YaSTie out of the shy girl I was before ;-) Writing it from scratch was the best way to understand how the components of YaST cooperate together (if you want to know more on this topic, here is some useful reading for you). However, it took almost two releases of openSUSE to discover the fundamental error I made in its design.
In YaST-speak, agent is the part of YaST module that grabs the configuration file, parses it and stuffs the data into some (more or less) reasonable data structure. As an inexperienced greenhorn, I wrote an agent in Perl that parses sudoers file. So far, so good. But as you might have already guessed, I used associative container (Perl hash, in this case) to hold sudoers data, not knowing that it will cause the rules to change their order, and mess everything up if the user is unlucky enough.

Cleaning up the mess


Now I had two options how to make yast2-sudo behave and preserve the order of sudo rules:
  1. Use Perl's tied hash in agent and make sure YCP preserves the order as well
  2. Store sudoers data in non-associative container and rewrite YCP stuff not to use maps, but lists
At the end, I opted for the second way. Though it was much more work for me, it was somehow cleaner solution. It meant throwing away not only parts of Perl agent, but also major portion of business logic, so that only user interface remained, but on the other hand, it simplified things quite a bit. The final diff has 1597 lines and I hope it will make it to openSUSE 11.1 RC1. Then, please, give it a good deal of testing and do not forget to report bugs :-)
One more thing, though. I've added a small aid to user interface for you to help you prioritize your sudo rules:


Comments

(Anonymous) wrote:
Nov. 24th, 2008 07:43 am (UTC)
drag and drop feature
When I saw this blog about moving rules and Up/Down I get idea on interesting feature, add to yast widget for table that support drag and drop changing of entries. So you can use mouse to move rule to its possition. Maybe I spend some ITO to try it. JR
[info]hedgehogpainter wrote:
Nov. 24th, 2008 09:47 am (UTC)
Re: drag and drop feature
Heh, I must admit I borrowed the idea of moving lines up/dwon from bootloader :)
What I really miss in YCP-ui bindings (and libyui) is that one can't easily modify/replace one item at specific position in the table (and alike widgets), items can by replaced only all at once. That could be very pleasant side effect of implementing drag-and-drop feature ...

Latest Month

June 2009
S M T W T F S
 123456
78910111213
14151617181920
21222324252627
282930    

Page Summary

Powered by LiveJournal.com
Designed by Tiffany Chow