Ταυτότητα διεργασίας#
Έξι μεταβλητές εκθέτουν τι θεωρεί το λειτουργικό σύστημα γι” αυτή τη διεργασία: το όνομά της, το PID της, και τα πραγματικά-και-effective IDs χρήστη και ομάδας. Είναι πιο χρήσιμες σε σενάρια που χρειάζεται να δρουν βάσει ποιος τα εκτελεί, να απορρίπτουν προνόμια μετά από προνομιακή λειτουργία, ή να διακρίνουν γονέα από παιδί μετά από fork.
Μεταβλητή | Τι κρατά | Τροποποιήσιμο |
|---|---|---|
| Όνομα προγράμματος (και εμφάνιση | ναι |
| PID τρέχουσας διεργασίας | όχι (μόνο ανάγνωση) |
| Πραγματικό UID | ναι (προνομιακό) |
| Effective UID | ναι (προνομιακό) |
| Πραγματικό GID (και συμπληρωματικά) | ναι (προνομιακό) |
| Effective GID (και συμπληρωματικά) | ναι (προνομιακό) |
$0 - όνομα προγράμματος#
Το όνομα του προγράμματος που εκτελείται, όπως μεταβιβάστηκε στο argv[0]:
print "Started by: $0\n"; # e.g. "/usr/local/bin/myscript.pl"
Η ανάθεση στη $0 αλλάζει πώς εμφανίζεται η διεργασία στην έξοδο ps και παρόμοιες λίστες - βολικό για σενάρια μεγάλης διάρκειας που κάνουν fork και θέλουν κάθε παιδί να είναι αναγνωρίσιμο:
$0 = "myserver: worker pid $$"; # appears in `ps` as that string
Η αλλαγή γράφει στην περιοχή μνήμης διεργασίας που διαβάζει η ps σε Linux. Παλαιότερες παραλλαγές Unix και τα Windows έχουν διαφορετικούς κανόνες· η PetaPerl ακολουθεί ακριβώς τη συμπεριφορά της perl5. Η ανάγνωση της $0 επιστρέφει πάντα την τρέχουσα τιμή, συμπεριλαμβανομένων τυχόν τροποποιήσεων.
Η $0 δεν είναι το όνομα προγράμματος όπως το βλέπει το σενάριο - για αυτό, δείτε @ARGV (που έχει τη $0 ως συνοδό στη θέση [-1] του αρχικού argv, όχι ως @ARGV[0]).
$$ - αναγνωριστικό διεργασίας#
Μόνο ανάγνωση. Το PID της τρέχουσας διεργασίας.
print "Running as PID $$\n";
open my $log, '>>', "/var/log/myapp.log" or die $!;
print $log "[$$] starting...\n";
Μετά τη fork, το παιδί βλέπει διαφορετικό $$ από τον γονέα - αυτός είναι ο τυπικός τρόπος να ξέρετε σε ποια πλευρά της fork βρίσκεστε:
my $pid = fork;
defined $pid or die "fork: $!";
if ($pid == 0) {
# child
print "child: my pid is $$\n";
exit 0;
}
# parent
print "parent: my pid is $$, child is $pid\n";
waitpid $pid, 0;
Το ψευδώνυμο English $PROCESS_ID (και το πλεονασματικό $PID) λειτουργούν ίδια με τη $$.
Για να πάρετε το PID του γονέα, χρησιμοποιήστε τη getppid.
Πραγματικά έναντι effective IDs - τι σημαίνουν#
Κάθε διεργασία Unix έχει δύο IDs χρήστη και δύο IDs ομάδας:
Το πραγματικό UID/GID είναι ποιος είστε - συνήθως ο χρήστης που εκκίνησε το πρόγραμμα.
Το effective UID/GID είναι αυτό που ελέγχει ο πυρήνας για αποφάσεις δικαιωμάτων - τυπικά ίδιο με το πραγματικό, αλλά διαφορετικό για προγράμματα setuid και setgid.
Όταν εκτελείτε ένα εκτελέσιμο setuid-root, ο πυρήνας θέτει το effective UID σε root (ο ιδιοκτήτης του αρχείου) αλλά αφήνει το πραγματικό UID ως δικό σας. Το πρόγραμμα μπορεί στη συνέχεια να ενεργεί ως root για την προνομιακή λειτουργία, και είτε να απορρίψει προνόμια θέτοντας το effective πίσω στο πραγματικό, είτε να ελέγξει ποιος πραγματικά το εκκίνησε διαβάζοντας το πραγματικό UID.
$< - πραγματικό UID#
print "Invoked by user with UID $<\n";
Η $< είναι το πραγματικό UID. Σχεδόν ποτέ δεν αλλάζει κατά τη διάρκεια ζωής μιας διεργασίας· οι περιπτώσεις που μπορεί είναι πολύ περιορισμένες (μόνο ο root μπορεί να το αλλάξει, και μόνο μέσω συγκεκριμένων κλήσεων συστήματος).
Μνημονικό: το UID από όπου ήρθατε.
$> - effective UID#
print "Privileges currently effective: UID $>\n";
Η $> είναι το effective UID - αυτό που μετράει για ελέγχους δικαιωμάτων. Σε πρόγραμμα setuid, αρχικά είναι το UID του ιδιοκτήτη αρχείου· σε κανονικό πρόγραμμα, ισούται με τη $<.
Μνημονικό: το UID προς το οποίο πήγατε.
Το κλασικό μοτίβο setuid#
Ένα βοηθητικό setuid-root που χρειάζεται να κάνει μία προνομιακή λειτουργία και μετά να απορρίψει προνόμια ώστε ο υπόλοιπος κώδικας να τρέξει ως ο πραγματικός χρήστης:
#!/usr/bin/perl
use strict;
use warnings;
# Privileged work first - we still have effective root.
open my $secret, '<', '/etc/some-restricted-file'
or die "open: $!";
my $config = do { local $/; <$secret> };
close $secret;
# Drop privileges. From now on, both real and effective UID
# are the invoking user's, so any subsequent open/system call
# is checked against the real user.
$> = $<; # set effective UID to real UID
$) = $(; # set effective GID to real GID
# Now run the rest of the script as the real user.
do_user_work($config);
Η μόνιμη απόρριψη προνομίων πρέπει να θέσει το effective πριν το πραγματικό (ή και τα δύο ταυτόχρονα μέσω POSIX::setuid())· στα περισσότερα συστήματα αν θέσετε πρώτα το πραγματικό θα κλειδωθείτε εκτός της επαναφοράς του effective. Ελέγξτε τη $! μετά από κάθε ανάθεση - ο πυρήνας απορρίπτει μη εξουσιοδοτημένες αλλαγές:
$> = $<;
die "could not drop privileges: $!" if $> != $<;
Εναλλαγή πραγματικού και effective#
Όταν θέλετε να αλλάξετε προσωρινά το effective ID για μία μόνο λειτουργία και μετά να το επαναφέρετε:
my $saved_uid = $>;
$> = $<; # become real user
my $ok = open(my $fh, '<', $user_path);
$> = $saved_uid; # back to effective
$ok or die "open $user_path: $!";
Αυτό λειτουργεί μόνο σε συστήματα που υποστηρίζουν setreuid() - τα περισσότερα σύγχρονα Unix, συμπεριλαμβανομένου του Linux. Η POSIX::seteuid() από το άρθρωμα POSIX είναι η ρητή και φορητή γραφή.
$( - πραγματικό GID (με συμπληρωματικές ομάδες)#
Η $( επιστρέφει το πραγματικό GID ακολουθούμενο από κάθε συμπληρωματική ομάδα στην οποία ανήκει η διεργασία, όλα διαχωρισμένα με κενά:
print "Real GID list: $(\n"; # e.g. "1000 1000 4 27 100 999"
# primary + supplementaries
my @groups = split ' ', $(;
my $primary = $groups[0];
Ο πρώτος αριθμός είναι το κύριο GID· τα υπόλοιπα είναι συμπληρωματικές ομάδες (από /etc/group). Η ανάθεση στη $( θέτει το GID· η σύνταξη δέχεται έναν μόνο αριθμό:
$( = 100; # become primary GID 100
$) - effective GID (με συμπληρωματικές ομάδες)#
Ίδια μορφή με τη $(, αλλά για το effective GID:
print "Effective GID list: $)\n";
# Drop both real and effective GID to a specific gid:
$( = $) = "$gid $gid";
Οι έλεγχοι δικαιωμάτων χρησιμοποιούν το effective GID και τις συμπληρωματικές· η λίστα πραγματικού GID είναι ενημερωτική.
Πότε τα χρησιμοποιείτε πραγματικά#
Ο καθημερινός κώδικας Perl δεν αγγίζει UIDs και GIDs. Οι ρεαλιστικές περιπτώσεις χρήσης είναι:
Σενάρια setuid που χρειάζεται να απορρίψουν προνόμια. Τυπικό ιδίωμα που φαίνεται παραπάνω.
Εκκίνηση δαίμονα που τρέχει ως root για δέσμευση προνομιακής θύρας, και μετά απορρίπτει προνόμια πριν εξυπηρετήσει αιτήσεις:
socket(my $srv, ...) or die $!; bind($srv, sockaddr_in(80, INADDR_ANY)) or die $!; listen($srv, SOMAXCONN) or die $!; # Now drop to nobody:nogroup before accepting connections. my (undef, undef, $u, $g) = getpwnam('nobody'); $) = "$g $g"; $( = $g; $> = $u; $< = $u;
Έλεγχος προνομίων σε σενάριο που πρέπει να αρνείται να τρέξει ως root:
die "do not run me as root\n" if $< == 0.
Αλληλεπίδραση με tainted-mode#
Υπό -T, η Perl παρατηρεί όταν τα πραγματικά και effective IDs διαφέρουν (δηλ. το σενάριο είναι setuid) και ενεργοποιεί επιπρόσθετους ελέγχους: ορισμένες λειτουργίες αρνούνται tainted ορίσματα, και η διαδρομή αναζήτησης για system() και exec() περιορίζεται. Η σχετικότητα εδώ είναι ότι αν η -T είναι ενεργή αποφασίζεται βάσει $< έναντι $> κατά την εκκίνηση.
Δείτε επίσης#
getppid- το PID του γονέα, σε ζεύγος με τη$$.fork- μετά την οποία η$$διαφέρει σε γονέα και παιδί.getpwuid,getgrgid- μετατρέπουν τα αριθμητικά IDs σε ονόματα χρηστών και ομάδων.POSIX::setuid- ο φορητός, ρητός τρόπος αλλαγής UIDs μαζί.Μεταβλητές σφάλματος - η
$!μετά από ανάθεση$</$>σας λέει γιατί ο πυρήνας αρνήθηκε.