> Proseslerin Yetki Kavramları: Biz modern Linux sistemlerinde yetki gerektiren programları "sudo" komutuyla ("sudo" bir programdır) çalıştırmaktayız. Bir programı "sudo" ile çalıştırdığımızda yaratılan prosesin etkin kullanıcı id'si 0 ve etkin grup id'si, gerçek kullanıcı id'si ve gerçek grup id'si 0 olur. Kullanıcı id'si sıfır olan kullanıcı genellikle "root" ismiyle bulundurulmaktadır. Pek çok UNIX türevi sistemde aynı zamanda 0 numaralı bir grup da bulunmaktadır. Bu da genellikle "root" biçiminde isimlendirilmektedir. Yani "root" isminde hem bir kullanıcı hem de bir grup bulunmaktadır. Anımsanacağı gibi prosesin etkin grup id'sinin 0 olması hiçbir erişim ayrıcalığı oluşturmamaktadır. Asıl olan "etkin kullanıcı id'sinin" 0 olmasıdır. Anımsanacağı gibi Linux sistemleri install edilirken kurulum sırasında kullanıcının girdiği isimle aynı olan bir kullanıcı ismi ve grup ismi oluşturulmaktadır. Kullanıcı ve grup id'lerinin farklı isim alanlarında olduğunu anımsayınız. Örneğin 1000 numaralı bir kullanıcı id'si de olabilir, bir grup id'si de olabilir. Ancak aynı id'ye sahip birden fazla kullanıcı ya da grup olmamalıdır. (Tabii bu durum aslında yasak değildir. Yani biz örneğin etkin kullanıcı id'si 0 olan başka bir kullanıcı da yaratabiliriz. Çekirdek, isimleri değil numaraları işleme sokmaktadır.) Şimdi de "sudo" olmaya ilişkin alt maddeleri inceleyelim: >> Bir programı "sudo" ile çalıştırdığımızda çalıştırılan programa ilişkin prosesin etkin ve gerçek kullanıcı id'sinin ve grup id'sinin 0 olduğunu belirtmiştik. Yani "sudo" ile bir programı çalıştırdığımızda adeta program "root" kullanıcısı tarafından çalıştırılıyormuş gibi bir etki oluşmaktadır. Linux dağıtımlarının bir bölümünde "root" kullanıcısı ile "login" olma güvenlik gerekçesiyle engellenmiştir. Ancak bu dağıtımlarda "root" olarak "bash" komut satırına düşmek istiyorsanız "sudo" ile "bash" programını çalıştırabilirsiniz. Örneğin: $ sudo bash Bir programı "sudo" ile çalıştırmak istediğimizde bizden önce kendi kullanıcımıza ilişkin parola istenmektedir. Ancak bir süre içerisinde artık "sudo" yapıldığında parola istenmeyecektir. >> Her kullanıcı "sudo" yapamamaktadır. "sudo" yapabilen kullanıcılara "sudoer" denilmektedir. Sistem kurulurken kurulum programı tarafından yaratılan kullanıcı "sudoer" durumundadır. Bir kullanıcıyı "sudoer" yapabilmek için en basit yol o kullanıcıyı "/etc/group" dosyasında "sudo" grubuna eklemektir. Örneğin kursun yapıldığı makinedeki "/etc/group" dosyasının "sudo" grup satırı şöyledir: sudo:x:27:kaan Buradan "sudo" grubunun grup id'sinin 27 olduğu ve "kaan" kullanıcısının bu gruba ek grup (supplemantary group) biçiminde dahil olduğu görülmektedir. Şimdi biz "ali" kullanıcısının da "sudo" yapabilmesini istiyorsak "ali" kullanıcı ismini de aşağıdaki gibi satıra eklemeliyiz: sudo:x:27:kaan,ali Aslında bu işlem komut satırında "usermod" komutu ile "-a" ve "-G" seçenekleri kullanılarak da yapılabilmektedir. Örneğin: $ sudo usermod -a -G sudo student Tabii bu komutu şöyle de uygulayabilirdik: $ sudo usermod -aG sudo student Kullanıcıya "sudo" yeteneği verebilmenin diğer bir yolu da "/etc/sudoers" dosyasına yeni bir giriş eklemektir. Biz burada bu yöntem üzerinde durmayacağız. >> Bir programı "sudo" ile çalıştırdığımızda üst prosesin çevre değişkenleri default durumda çalıştırılan programa aktarılmamaktadır. Örneğin biz kabukta bazı çevre değişkenleri tanımlamış olalım. "sudo" ile bir program çalıştırdığımızda bu çevre değişkenleri default durumda yaratılan prosese aktarılmayacaktır. Bunu basit bir biçimde deneyimleyebiliriz. Bunun için önce komut satırında aşağıdaki gibi çevre değişkenleri oluşturabiliriz: $ export XXX=100 $ export YYY=100 Şimdi "env" komutunu uygularsak bu çevre değişkenlerinin yaratılmış olduğunu görürüz. Ancak "sudo env" komutunu uyguladığımızda bu çevre değişkenleri alt prosese aktarılmayacağı için onları göremeyeceğiz. Ancak kabuğun çevre değişkenleri çalıştırılan programa ilişkin prosese "-E" seçeneği kullanılarak aktarılabilmektedir. Örneğin: $ sudo -E env Ancak "-E" seçeneği de güvenlik gerekçesiyle PATH çevre değişkenini yaratılan prosese geçirmemektedir. >> sudo işlemi konusunda "/etc/sudoers" dosya da etkilidir. Bu dosyaya girişler eklenerek belli bir proses sudoer yapılabilmektedir. Bu dosyanın başındaki aşağıdaki satırlar önemlidir: Defaults env_reset Defaults mail_badpass Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" Defaults use_pty Buradaki "env_reset" satırında "env_reset" yerine "env_keep" getirilirse defaut olarak burada belirtilen çevre değişkenleri sudo ile çalıştırılan proseslere aktarılmaktadır. Örneğin: Defaults env_keep += "XX YY" Burada sudo ile bir program çalıştırıldığında alt prosese XX ve YY çevre değişkenleri aktarılacaktır. env_keep += "*" tüm çevre değişkenlerinin aktarılacağı anlamına gelir. Ancak yine PATH çevre değişkeni bunun dışındadır. Örneğin: Defaults env_keep += "*" Eğer PATH çevre değişkeninin de yaratılan prosese aktarılması isteniyorsa secure_path satırı kaldırılmalı ya da # ile yorum satırı içerisine alınmalıdır. Örneğin: Defaults env_reset Defaults mail_badpass # Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin" Defaults use_pty Öte yandan Klasik UNIX tasarımında erişim bakımından "ya hep ya hiç" sistemi uygulanmaktadır. Yani sıradan kullanıcılar sistemle ilgili önemli dosyalara erişemezler ve sistemde davranış değişikliklerine yol açabilecek sistem fonksiyonlarını çağıramazlar. Ancak prosesin kullanıcı id'si 0 ise (yani proses "root" hakkına sahipse) her şeyi yapabilirler. Bazı UNIX türevi sistemlerde bu "ya hep ya hiç" sistemi "yeteneklilik (capability)" denilen kavramla yumuşatılmıştır. Bu sistemlerde çeşitli yetenekler vardır. Bir proses root hakkına sahip olmasa bile bu yeteneklerden bazılarına sahip olabilir. Bu durumda bu yetenekleri gerektiren bazı işlemleri yapabilir. Yeteneklilik (capability) konusu POSIX standartlarına sokulmamıştır. POSIX standartları genel olarak "uygun öncelik (appropriate priviledge)" terimini kullanmaktadır. Burada uygun öncelik prosesin root önceliğinde olması ya da ilgili işlemi yapacak yeteneğe sahip olması anlamına gelmektedir. Yeteneklilik konusu POSIX tarafından standardize edilmiş bir konu olmadığı için farklı UNIX türevi sistemlerde farklı biçimlerde gerçekleştirilmiştir. Ancak bunlar arasında en geniş tasarıma sahip olan Linux sistemleridir. * Örnek 1, Bir thread'in sahip olabileceği yetenekler proses kontrol bloğunda bitsel bir biçimde tutulmaktadır. Linux'taki yetenekler şunlardır: CAP_CHOWN: Dosya sahibi ve grup değiştirme yeteneği. CAP_DAC_OVERRIDE: Dosya erişim kontrollerini geçme yeteneği (okuma/yazma/çalıştırma). CAP_DAC_READ_SEARCH: Dosya okuma ve dizin arama izinlerini geçme yeteneği. CAP_FOWNER: Dosya üzerinde, sahibi tarafından yapılan işlemleri yapma yeteneği (örneğin dosyanın erişim özelliklerinin değiştirilmesi gibi) CAP_FSETID: Dosyanın UID veya GID’sini değiştirme yeteneği. CAP_KILL: Diğer proseslere sinyal gönderme yeteneği. CAP_SETGID: Kullanıcı grubunu değiştirme yeteneği. CAP_SETUID: Kullanıcı kimliğini değiştirme yeteneği. CAP_SETPCAP: Process yetenekliliğini değiştirme yeteneği. CAP_SETFCAP: Bir dosyanın yeteneklerini değiştirme yeteneği CAP_LINUX_IMMUTABLE: Bir dosyanın değiştirilemez (immutable) hale getirilmesini sağlama yeteneği. CAP_NET_BIND_SERVICE: Düşük numaralı portlara (1024'ten küçük) bağlanma yeteneği. CAP_NET_BROADCAST: Ağda broadcast (yayın) yapma yeteneği. CAP_NET_ADMIN: Ağ ayarlarını yönetme, ağ arayüzlerini yapılandırma yeteneği. CAP_NET_RAW: Düşük seviyeli ağ işlemleri yapabilme yeteneği. CAP_IPC_LOCK: Bellek üzerinde kilit (lock) işlemleri yapma yeteneği. CAP_IPC_OWNER: IPC (Inter-process communication) kaynaklarının sahipliğini değiştirme yeteneği. CAP_SYS_MODULE: Linux modüllerini yükleme ve kaldırma yeteneği. CAP_SYS_RAWIO: Donanımsal I/O portlarına erişme yeteneği CAP_SYS_CHROOT: chroot (root dosya sistemi değiştirme) işlemi yapma yeteneği. CAP_SYS_PTRACE: Diğer süreçleri izleme ve kontrol etme yeteneği (örneğin, hata ayıklama). CAP_SYS_PACCT: Process accounting verilerini okuma veya yazma yeteneği. CAP_SYS_ADMIN: Sistem yöneticisi yetkileri (modül yükleme, dosya sistemi değiştirme vb.). CAP_SYS_BOOT: Sistemi yeniden başlatma veya açma yeteneği. CAP_SYS_NICE: Diğer proseslerin nice değerini değiştirme yeteneği. CAP_SYS_RESOURCE: Sistem kaynaklarını yönetme ve limitlerini ayarlama yeteneği. CAP_SYS_TIME: Sistemin saatini değiştirme yeteneği. CAP_SYS_TTY_CONFIG: TTY (terminal) ayarlarını değiştirme yeteneği. CAP_MKNOD: Aygıt dosyaları oluşturma yeteneği. CAP_LEASE: Dosya kiralama (lease) mekanizmasını kullanma yeteneği. CAP_AUDIT_WRITE: Audit (denetim) sistemine yazma yeteneği. CAP_AUDIT_CONTROL: Audit sistemi yapılandırmalarını değiştirme yeteneği. CAP_SETFCAP: Dosya yetenekiliğini (capabilities) ayarlama yeteneği. CAP_MAC_OVERRIDE: MAC (Mandatory Access Control) politikalarını geçme yeteneği. CAP_MAC_ADMIN: MAC politikalarını yönetme yeteneği. CAP_SYSLOG: Sistem günlüklerini (log) okuma veya yazma yeteneği. CAP_WAKE_ALARM: Sistemi uyanma alarmıyla uyandırma yeteneği. CAP_BLOCK_SUSPEND: Sistemin uykuya geçmesini engelleme yeteneği. CAP_AUDIT_READ: Audit verilerini okuma yeteneği. CAP_PERFMON: Performans izleme (profiling) yeteneği. CAP_BPF: BPF (Berkeley Packet Filter) programlarını yükleme ve çalıştırma yeteneği. CAP_CHECKPOINT_RESTORE: Bir süreç için checkpoint alma ve geri yükleme yeteneği. Örneğin biz kill fonksiyonu kabaca yalnızca kendimizin oluşturduğu proseslere sinyal gönderebiliriz. Ancak eğer prosesimizin CAP_KILL yetenekliliği varsa bu durumda tıpkı root prosesi gibi başka proseslere de sinyal gönderebiliriz. Linux sistemlerindeki yeteneklilik (capability) konusu maalesef biraz karmaşık bir konudur. İzleyen paragraflarda konunun ayrıntılarına gireceğiz. Aslında Linux sistemlerinde yeteneklilik proses temelinde değil thread temelinde uygulanmaktadır. Yani prosesin farklı thread'leri farklı yeteneklere sahip olabilmektedir. Ancak pratikte prosesin tüm thread'leri genellikle aynı yeteneklere sahip olur. Yetenekler proses kontrol bloğunda bitsel düzeyde tutulmaktadır. Yani yukarıdaki listedeki CAP_XXXX sembolik sabitleri aslında tüm bitleri 0 olan yalnızca tek biti 1 olan sembolik sabitlerdir. Eskiden proses kontrol bloğunda yetenekleri tutan nesneler 32 bitlik nesnelerdi. Sonra çeşitli yetenekler de sisteme eklenince bunlar 64 bite yükseltildiler. Bugünkü Linux çekirdeklerinde therad'e ilişkin yetenekler task_struct yapısının cred isimli elemanının gösterdiği struct cred yapısında tutulmaktadır. Bir thread'in dört grup yetenekleri vardır: -> Etkin yetenekler (effective capabilities), -> İzin verilen yetenekler (permitted capabilities), -> Aktarılan yetenekler (inheritable capabilities), -> Sarmalayan yetenekler (bounding capabilities). Test işlemlerine etkin yetenekler sokulmaktadır. Örneğin thread'imizin başka bir prosese sinyal gönderebilmesi için thread'imizin etkin yetenekleri arasında CAP_KILL bulunuyor olması gerekir. İzin verilen yetenekler (permitted capabilities) etkin yetenekler için bir üst küme oluşturmaktadır. Yani bir thread'in kendisinin etkin yetenekleri ancak izin verilen yetenekleri kadar olabilir. Thread'in bir etkin yetenek kümesinde bir yetenek yoksa ancak izin verilen kümesinde varsa proses izin verilen kümesi içerisindeki o yeteneği etkin kümesine taşıyabilir. Ancak izin verilen kümesini genişletemez. Başka bir deyişle thread'in etkin yetenek kümesi izin verilen yetenek kümesinin bir alt kümesi biçimindedir. Aktarılabilen yetenek kümesi thread bir programı çalıştırdığında onun izin verilen kümesine dahil edilebilecek yetenekleri belirtmektedir. Benzer biçimde thread'in sarmalayan yetenekleri de izin verilen yeteneklerin belirlenmesi sırasında etki göstermektedir. Bu konu izleyen paragraflarda ele alınacaktır. Biz yukarıda Linux çekirdeğinin yetenekleri thread temelinde işleme soktuğunu belirttik. Yetenekler fork işlemi sırasında ya da pthread_create işlemi sırasında (bu da bir çeşit fork işlemi gibidir) üst thread'ten alt thread'e aktarılmaktadır. Yani aslında default durumda bir prosesin tüm thread'leri aynı yeteneklere sahiptir. Fakat biz istersek prosesin belli bir thread'inin yeteneklerini diğer thread'lere dokunmadan değiştirebiliriz. Üst proses fork uyguladığında onun "etkin", "izin verilen" ve "aktarılan" yeteneklerinin hepsi alt prosese aktarılmaktadır. Herhangi bir önceliğe sahip olmayan sıradan proseslerin etkin, izin verilen ve aktarılan yetenek kümesinde hiçbir yetenek yoktur. Örneğin biz bir terminal açıp orada çalışan kabuk programının (bash) yetenek kümelerine baksak bu üç yetenek kümesinin de boş küme olduğunu görürüz. Linux sistemlerinde thread'in yeteneklerini elde etmek için ve onu set etmek için sys_getcap ve sys_setcap isimli iki sistem fonksiyonu bulundurulmuştur. Ancak libc kütüphanesinde bu sistem fonksiyonlarını çağıran fonksiyonlar bulunmamaktadır. Eğer bu sistem fonksiyonlarını çağıracaksanız bunların numaralarını belirterek syscall isimli fonksiyonla çağırabilirsiniz. Ancak bu sistem fonksiyonlarının kullanımı biraz zahmetlidir. Bu fonksiyonlar yerine bu sistem fonksiyonları çağrılarak yazılmış olan "libcap" isimli bir kütüphane de bulunmaktadır. Sistem programcıları genellikle sys_getcap ve sys_setcap fonksiyonlarını kullanmak yerine zaten bunları kullanan "libcap" kütüphanesini kullanmayı tercih etmektedir. Ancak bu "libcap" kütüphanesi default durumda genellikle yüklü olmaz. Bu kütüphaneyi Debian türevi sistemlerde aşağıdaki gibi yükleyebilirsiniz. $ sudo apt install libcap2 libcap-dev Bir prosesin yetenek kümeleri "/proc//status" dosyasından elde edilebilir. Buradaki yeteneklere ilişkin bilgiler aşağıdaki gibi gözükecektir: CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 000001ffffffffff Bu örnekte prosesin bütün yetenek kümesi boştur. Yani proses hiçbir yeteneğe sahip değildir. Anımsanacağı gibi aslında "proc" dosya sisteminde her için bir dizin yaratılmaktadır. Yani "/proc//status" yol ifadesindeki pid aslında ilgili thread'in gettid fonksiyonu ile elde edilen pid numarasıdır. (POSIX standartlarında thread'lerin pid değerinin olmadığını ancak Linux sistemlerinde Linux çekirdeğinin thread'leri prosesler gibi oluşturduğu için thread'lerin de pid değerlerinin olduğunu anımsayınız. Yine anımsanacağı gibi proc dosya sisteminde ana thread'e ilişkin "task" dizininde prosesin thread'lerinin pid numaraları bulunmaktadır.) Bir thread'in yetenekleri programlama yoluyla "libcap" kütüphanesindeki cap_get_proc fonksiyonu ile elde edilebilir. >> "cap_get_proc" : Fonksiyon kendisini çağıran thread'in yetenek bilgilerini temsil eden cap_t türünden bir handle değeri ile geri döner. #include cap_t cap_get_proc(void); cap_t türü aşağıdaki gibi bildirilmiştir: typedef struct __user_cap_header_struct { unsigned int version; // Yapı versiyonu pid_t pid; // İlgili işlem ID'si } __user_cap_header_t; typedef struct __user_cap_data_struct { unsigned int effective; // Etkin yetenekler unsigned int permitted; // İzin verilen yetenekler unsigned int inheritable; // Miras alınan yetenekler } __user_cap_data_t; typedef struct __user_cap_struct { __user_cap_header_t header; // Başlık bilgisi __user_cap_data_t data[2]; // Yetenek verisi (genellikle iki set) } *cap_t; * Örnek 1, cap_t caps; if ((caps = cap_get_proc()) == NULL) exit_sys("get_cap_proc"); >> "cap_get_pid" : Bu fonksiyon ise pid değeri verilen prosesin (ya da thread'in) yeteneklerini elde etmektedir. Fonksiyonun prototipi şöyledir: #include cap_t cap_get_pid(pid_t pid); cap_t türünün bir yapı adresini belirttiğine dikkat ediniz. Bu fonksiyonlar kendi içerisinde cap_t türünden bir yapı nesnesini dinamik olarak tahsis edip onun adresiyle geri dönemktedir. Bu dinamik alaın boşaltımı cap_free fonksiyonu ile yapılmalıdır. >> "cap_free" : #include int cap_free(void *obj_d); Aslında cap_free fonksiyonu yalnızca free fonksiyonunu çağırmaktadır. * Örnek 1, if ((caps = cap_get_proc()) == NULL) exit_sys("get_cap_proc"); /* ... */ cap_free(caps); Her ne kadar cap_get_pid fonksiyonu sanki proses id alıyor gibiyse de yukarıda da belirttiğimiz gibi yetenekler aslında prosese değil thread'e özgüdür. Biz bu fonksiyona bir prosesin pid değerini geçirdiğimizde o prosesin ana thread'ine ilişkin yetenekleri elde etmiş oluruz. Belli bir thread'in bilgileri elde edilecekse doğrudan gettid fonksiyonu ile thread'in pid değeri elde edilebilir. cap_get_proc fonksiyonu ise o anda çalışmakta olan thread'in yeteneklerini elde etmekte kullanılmaktadır. Thread'in yetenekleri elde edildiğinde onların yazdırılması da bir sorundur. Bu nedenle "libcap" kütüphanesinde cap_to_text isimli bir fonksiyon bulundurulmuştur. >> "cap_to_text" : Fonksiyonun prototipi şöyledir: #include char *cap_to_text(cap_t caps, ssize_t *length_p); Fonksiyonun birinci parametresi cap_t türünden handle değerini, ikinci parametresi verilen yazının uzunluğunun yerleştirileceği nesnenin adresini belirtir. İkinci parametre NULL adres geçilebilir. Bu durumda fonksiyon böyle bir yerleştirme yapmaz. Fonksiyon oluşturulan yazının adresine geri dönmektedir. Yine bu adresin de cap_free kullanımdan sonra boşaltılması gerekir. Eğer cap_to_text fonksiyonunda yalnızca "=" biçiminde bir yazı elde ediliyorsa bu durum thread'in herhangi bir yeteneğe sahip olmadığı anlamına gelmektedir. Aşağıda o anda çalışmakta olan thread'in yetenekleri ekrana yazdırılmıştır. * Örnek 1, #include #include #include void exit_sys(const char *msg); int main(void) { cap_t caps; char *captext; if ((caps = cap_get_proc()) == NULL) exit_sys("cap_get_proc"); if ((captext = cap_to_text(caps, NULL)) == NULL) { cap_free(caps); exit_sys("cap_to_text"); } printf("%s\n", captext); cap_free(captext); cap_free(caps); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } Bir thread'in yetenekleri onu yaratan thread'ten aktarılmaktadır. Yani genel olarak bir proses bir thread yarattığında ya da fork işlemi yaptığında yaratılan thread ya da proses bu işlemi uygulayan thread ile aynı yeteneklere sahip olur. Sıradan bir prosesin ya da onun thread'lerinin yeteneklerine baktığımızda tipik olarak şöyle olduğunu görürüz: CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 000001ffffffffff Buradan çıkan sonuç şudur: Sıradan bir prosesin ya da thread'in aktarılan, izin verilen ve etkin yetenek kümesi boş kümedir. Yani bu küme hiçbir yeteneği kapsamamaktadır. Ancak sarmalayan yeteneklerin (bounding capabilities) bütün bitlerinin 1 olduğunu görüyorsunuz. (Her ne kadar burada yüksek anlamlı bitlerin bazılarını 0 görüyor olsanız da zaten Linux o bitleri kullanmamaktadır.) Etkin kullanıcı id'si 0 olan proseslerin hiçbir engele takılmadığını anımsayınız. Linux çekirdeği önce prosesin etkin kullanıcı id'sini kontrol etmektedir. Eğer prosesin etkin kullanıcı id'si 0 ise zaten herhangi bir yetenek kontrolü yapmasına gerek kalmamaktadır. Yani yetenekler ancak etkin kullanıcı id'si 0 olmayan prosesler için anlamlı bir özelliktir. Pekiyi sıradan bir prosesin tüm thread'lerinin yetenekleri boş küme olduğuna göre bu prosesler ve thread'ler nasıl yetenek kazanmaktadır? İşte bunun iki yolu olabilir: -> Yetenek kazandırabilecek yeteneğe sahip ya da etkin kullanıcı id'si 0 olan bir proses bir alt proses yaratıp onun yeteneklerini oluşturup sonra kullanıcı id'sini 0 olmaktan çıkartabilir. -> Aslında Linux sistemlerinde çalıştırılabilen dosyalara da yetenekler atanabilmektedir. Bir çalıştırılabilen dosya exec fonksiyonlarıyla çalıştırıldığında izleyen paragraflarda açıklanacağı gibi otomatik olarak proses bazı yeteneklere sahip olabilmektedir. Bu durum aslında daha önce gördüğümüz çalıştırılabilen dosyaların "set-user-id" ve "set-grup-id" bayraklarına benzemektedir. Örneğin thread'imizin yetenek kümeleri boş küme olsun. Ama biz "xxx" isimli programı exec fonksiyonlarıyla çalıştırmak isteyelim. Bir kez fork yapıp alt proseste exec yaptığımızda artık alt prosesimizin yetenekleri bu "xxx" dosyasında belirtilen yeteneklere sahip olabilmektedir. Yani prosesleirn ve thread'lerin yetenekleri aslında genellikle exec işlemi sırasında boş küme olmaktan çıkmaktadır. Pekiyi thread'imize belli bir yeteneği nasıl kazandırabiliriz? Bir thread etkin yeteneklerini ancak izin verilen yetenekler kadar artırabilir. O halde önce thread'imizin izin verilen yeteneklerini artırması gerekir. Bunun için sırasıyla şu işlemler yapılmalıdır: -> Önce içi boş bir yetenek nesnesi cap_init fonksiyonuyla oluşturulur. cap_init fonksiyonunun prototipi şöyledir: #include cap_t cap_init(void); cap_init fonksiyonu başarısızlık durumunda NULL adrese geri dönmektedir. * Örnek 1, ... cap_t newcaps; if ((newcaps = cap_init()) == NULL) exit_sys("cap_init"); ... -> Bu yetenek nesnesine cap_set_flag fonksiyonu ile çeşitli bayraklar eklenir. Fonksiyonun prototipi şöyledir: #include int cap_set_flag(cap_t cap_p, cap_flag_t flag, int ncap, const cap_value_t *caps, cap_flag_value_t value); Fonksiyonun birinci parametresi yeteneklerin set edileceği nesneyi belirtmektedir. İkinci parametre hangi yetenekler üzerinde işlem yapılacağını belirtir. Bu parametre aşağıdaki sembolik sabitlerden yalnızca birini içerebilir: CAP_EFFECTIVE CAP_INHERITABLE CAP_PERMITTED Fonksiyon tarafından set edilecek yetenekler dördüncü parametresi ile belirtilen dizinden elde edilmektedir. Yani programcı cap_value_t türünden bir dizi oluşturup bu dizinin içerisine yenetekleri yerleştirir. Fonksiyonun üçün parametresine de bu dizinin uzunluğunu geçirir. Fonksiyoun son parametresi aşağıdaki iki değerdne biri olarak girilir: CAP_CLEAR CAP_SET Fonksiyon başarı durumunda 0 değerine, başarısızlık durumunda -1 değerine geri dönemtedir. * Örnek 1, ... cap_value_t caparray[] = {CAP_CHOWN, CAP_KILL}; ... if (cap_set_flag(newcaps, CAP_PERMITTED, 2, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_flag(newcaps, CAP_EFEFCTIVE, 2, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } ... -> Oluşturulan yetenekler cap_set_proc fonksiyonu ile o anda çalışan thread'e aktarılır. cap_Set_proc fonksiyonun prototipi şöyledir: #include int cap_set_proc(cap_t cap_p); Fonksiyon hazırlanan capt_t nesnesini parametre olarak alır. Thread'in yeteneklerini set eder. Başarı durumunda 0 değerine başarısızlık durumunda -1 değerine geri döner. Örneğin: if (cap_set_proc(newcaps) == -1) { cap_free(newcaps); exit_sys("cap_set_proc"); } Şimdi de bu konuya ilişkin örnekleri inceleyelim: * Örnek 1, Aşağıda bir thread'in yeteneklerini değiştiren örnek bir program verilmiştir. Tabii cap_set_proc fonksiyonun da kullanılması için prosesin uygun önceliğe sahip olması gerekir. (Yani prosesin etkin kullanıcı id'si 0 olmalı ya da ilgili thread yetenek değiştirme yeteneği olan CAP_SYS_ADMIN yeteneğine sahip olmalıdır.) #include #include #include void exit_sys(const char *msg); int main(void) { cap_t newcaps; cap_value_t caparray[] = {CAP_CHOWN, CAP_KILL}; if ((newcaps = cap_init()) == NULL) exit_sys("cap_init"); if (cap_set_flag(newcaps, CAP_PERMITTED, 2, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_flag(newcaps, CAP_EFFECTIVE, 2, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_proc(newcaps) == -1) { cap_free(newcaps); exit_sys("cap_set_proc"); } cap_free(newcaps); printf("Ok\n"); getchar(); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 2, Aşağıda önce thread'in izin verilen ve etkin yetenekleri değiştirilmiş sonra da bu yenekler elde edilip yazdırılmıştır. Programın çalıştırılması sonucunda aşağdıaki gibi bir çıktı elde edilecektir: cap_chown,cap_kill=ep Programın kodları ise şu şekildedir: #include #include #include void exit_sys(const char *msg); int main(void) { cap_t newcaps, caps; cap_value_t caparray[] = {CAP_CHOWN, CAP_KILL}; char *captext; if ((newcaps = cap_init()) == NULL) exit_sys("cap_init"); if (cap_set_flag(newcaps, CAP_PERMITTED, 2, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_flag(newcaps, CAP_EFFECTIVE, 2, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_proc(newcaps) == -1) { cap_free(newcaps); exit_sys("cap_set_proc"); } cap_free(newcaps); if ((caps = cap_get_proc()) == NULL) exit_sys("cap_get_proc"); if ((captext = cap_to_text(caps, NULL)) == NULL) { cap_free(caps); exit_sys("cap_to_text"); } printf("%s\n", captext); cap_free(captext); cap_free(caps); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } * Örnek 3, Proses root olarak (etkin kullanıcı id'si 0 olarak) çalışıyor olsun. Prosesin etkin kullanıcı id'si seteuid fonksiyonuyla değiştirildiğinde prosesin tüm thread'lerinin etkin yenetenleri sıfırlanmaktadır. Ancak izin verilen yeteneklerine dokunulmamaktadır. Ancak prsesin gerçek kullanıcı id'si ile etkin kullanıcı id'si setuid fonksiyonu ile değiştirildiğinde prosesin tüm thread'lerinin hem izin verilen hem de etkin yetenekleri sıfırlanmaktadır. Bu konudaki ayrıntılar için "capabilities(7)" man sayfasına başvurabilirsiniz. Aşağıdaki örnekte seteuid fonksiyonundna sonra thread'in etkin yeteneklerinin düşürüldüğüne ilişkin bir örnek verilmiştir. Program çalıştırıldığında ekranda şunlar görünecektir: cap_chown,cap_kill,cap_setuid=ep cap_chown,cap_kill,cap_setuid=p Programın kodları ise şu şekildedir: #include #include #include #include void exit_sys(const char *msg); void disp_capability(void) { cap_t caps; char *captext; if ((caps = cap_get_proc()) == NULL) exit_sys("cap_get_proc"); if ((captext = cap_to_text(caps, NULL)) == NULL) { cap_free(caps); exit_sys("cap_to_text"); } printf("%s\n", captext); cap_free(captext); cap_free(caps); } int main(void) { cap_t newcaps; cap_value_t caparray[] = {CAP_CHOWN, CAP_KILL, CAP_SETUID}; if ((newcaps = cap_init()) == NULL) exit_sys("cap_init"); if (cap_set_flag(newcaps, CAP_PERMITTED, 3, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_flag(newcaps, CAP_EFFECTIVE, 3, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_proc(newcaps) == -1) { cap_free(newcaps); exit_sys("cap_set_proc"); } cap_free(newcaps); disp_capability(); if (seteuid(1000) == -1) exit_sys("setuid"); disp_capability(); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } Linux çekirdek kendi içerisinde aslında hep yetenek kontrolü yapmaktadır. Yani bir prosesin root önceliğinde olması (yani etkin kullanıcı id'sinin 0 olması) aslında izimn verilen ve etkin yeteneklerin hepsinin set edildiği anlamına gelmektedir. Dolayısıyla biz bir programı sudo ile çalıştırdıktan sonra onun etkin yeteneklerini değiştirirsek artık proses'in etkin kullanıcı id'si 0 olsa bile proses yetenek kaybedecektir. open fonksiyonu tarafından uygulanan erişim kontrollerinin thread yetenekleriyle bir ilgisi yoktur. Yani etkin user id'si 0 olan bir root proses yenek kümesi düşürülse bile yine open fonksiyonunda başarısız olmaz. Linux sistemlerinde yalnızca thread'lerin değil çalıştırılabilen dosyaların da yetenekleri vardır. Biz daha önce çalıştırılabilen bir dosyanın "set-user-id" ve "set-group-id" biçiminde isimlendirilen bayraklarının işlevlerini incelemiştik. Çalıştırılabilen bir dosyanın "set-user-id" bayrağı set edilmişse o programı exec yapan prosesin etkin kullanıcı id'si dosyanın kullanıcı id'si haline getiriliyordu. Aynı durum "set-group-id" bayrağı için de benzer biçimde işletiliyordu. Çalıştırılabilen bir dosyanın "set-group-id" bayrağı set edilmişse bu dosya exe yapıldığında prosesin etkin grup id'si dosyanın grup id'si oluyordu. İşte bu mekanizmanın benzeri yetenek temelinde de oluşturulmuştur. Çalıştırılabilen dosyaların da yetenekleri set edilmiş olabilir. Bu durumda bu dosyalar exec yapıldığında proses otomatik olarak o yeteneğe sahip hale gelmektedir. Örneğin bir program dosyasının CAP_KILL yeteneğinin set edilmiş olduğunu varsayalım. Ancak bu dosyanın etkin kullanıcı id'si root olmasın. Bu dosya çalıştırıldığında ilgili thread CAP_KILL yeteneğine sahip olacaktır. (Program birden fazla thread'e sahipse exec işlemiyle yalnızca exec yapan thread'in yaşamına devam ettiğini anımsayınız.) Ancak dosya yetenekleri konusunun da bazı ayrıntıları vardır. Dosya yetenekleri i-node elemanlarının içerisinde tutulmaktadır. Bu nedenle dosya sisteminin de dosya yeteneklerini tutma özelliğine sahip olması gerekir. Örneğin FAT dosya sistemlerinde böyle bir alan bulunmamaktadır. Yalnızca çalıştırılabilen dosyalar için yetenek kavramı söz konusudur. Tabii bir text dosyanın içerisinde bir script kodu da bulunabilir. Bu durumda bu dosyalar da çalıştırılabilen dosya olarak ele alınabilmektedir. Dosyaların yeteneklerini görüntülemek için "libcap" paketinde "getcap" isimli bir yardımcı program da bulunmaktadır. * Örnek 1, "ping" programının CAP_NET_RAW yeteneği vardır. "getcap" ile bu dosyanın yeteneklerini aşağıdaki gibi görüntüleyebiliriz: $ /usr/bin/ping cap_net_raw=ep /usr/bin/ping cap_net_raw=ep Tıpkı thread'ler gibi dosyaların da "izin verilen (permitted)", etkin (effective) ve aktarılan (inheritable) yetenekleri vardır. Dosyaların yetenekleri "setcap" isimli programla değiştirilebilmektedir. Programın örnek kullanımı şöyledir: $ sudo setcap "cap_net_bind_service=pei cap_net_raw=ep" sample Burada sample programına çeşitli yetenekler set edilmiştir. Bu yeneklerin tek bir komut satırı argümanı biçiminde verildiğine dolayısıyla da tırnak içeeisine alındığına dikkat edniz. Eğer dosyadan tüm yetenekler silinecekse komut aşağıdaki gibi kullanılabilir: $ sudo setcap = sample setcap programının kullanımına ilişkin ayrıntılar için man sayfalarına başvurabilirsiniz. * Örnek 1, "sample" isimli programımıza izin verilen ve etkin olarak CAP_KILL yeteneğini eklemek isteyelim. Bu işlemi şöyle yapabiliriz: $ sudo setcap "cap_kill=ep" sample Şimdi dosyanın yeteneğini "getcap" komutu ile görüntüleyelim: $ getcap sample sample cap_kill=ep Tabii bir dosyanın yeteneğini değiştirebilmek için ilgili thread'in de CAP_SETPCAP yeneğine sahip olması gerekir. root prosesleri tüm yeneklere sahipmiş gibi düşünmeliyiz. Tabii aslında dosyaların yeteneklerini elde etmek için ve onlara yetenek iliştirmek için Linux çekirdeğinde sistem fonksiyonları bulunmaktadır. "getcap" ve "setcap" programları libcap kütüphanesindeki cap_set_file ve cap_get_file fonksiyonları kullanılarak yazılmıştır. Bu fonksiyonlar da sys_setxattr, sys_fsetxattr, sys_lsetxattr ve sys_getxattr, sys_fgetxattr ve sys_lgetxattr, sistem fonksiyonları çağrılarak yazılmıştır. Bu sistem fonksiyonları için "libc" kütüphanesinde sarma fonksiyonlar bulunmaktadır. Ancak biz burada bu işlemleri "libcap" kütüphanesindeki cap_set_file ve cap_get_file fonksiyonlarını kullanarak yapacağız. Bu fonksiyonlardan, >> "cap_get_file" fonksiyonun prototipi şöyledir: #include cap_t cap_get_file(const char *path_p); Fonksiyon dosyanın yol ifadesini alır ve başarı durumunda dosyanın yetenek bilgilerine ilişkin cap_t nesnesine geri döner. Bu nesnenin kullanım bittikten sonra cap_free fonksiyonu ile boşaltılması gerekmektedir. Fonksiyon başarısızlık durumunda NULL adrese gerei dönmektedir. Örneğin: cap_t caps; if ((caps = cap_get_file("/usr/bin/ping")) == NULL) exit_sys("cap_get_file"); Aşağıda fonksiyonun kullanımına ilişkin örnek bir program verilmiştir. * Örnek 1, #include #include #include void exit_sys(const char *msg); void disp_cap(cap_t caps) { char *captext; if ((captext = cap_to_text(caps, NULL)) == NULL) { cap_free(caps); exit_sys("cap_to_text"); } printf("%s\n", captext); cap_free(captext); } int main(int argc, char *argv[]) { cap_t caps; if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } if ((caps = cap_get_file(argv[1])) == NULL) exit_sys("cap_get_file"); disp_cap(caps); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } >> "cap_set_file" fonksiyonun prototipi de şöyledir: #include int cap_set_file(const char *path_p, cap_t cap_p); Fonksiyonun birinci parametresi dosyanın yol ifadesini, ikinci parametresi dosyaya iliştirilecek yetenekleri belirtmektedir. Fonksiyon başarı durumunda 0 değerine başarısızlık durumunda -1 değerine geri dönmektedir. Tabii bir dosyaya yetenek set edebilmek için ya prosesin root olması (etkin kullanıcı id'sinin 0 olması) ya da prosesin CAP_SETFCAP yeteneğine sahip olması gerekmektedir. * Örnek 1, ... cap_t caps, newcaps; cap_value_t caparray[] = {CAP_CHOWN, CAP_KILL, CAP_SETUID}; if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } if ((newcaps = cap_init()) == NULL) exit_sys("cap_init"); if (cap_set_flag(newcaps, CAP_PERMITTED, 3, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_flag(newcaps, CAP_EFFECTIVE, 3, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_file(argv[1], newcaps) == -1) { cap_free(newcaps); exit_sys("cap_set_proc"); } cap_free(newcaps); Burada CAP_CHOWN, CAP_KILL, CAP_SETUID yenekleri "izin verilen" ve "etkin" yetenekler olarak dosyaya iliştirilmiştir. Aşağıda bu iki fonksiyonun ortak kullanılmasına ilişkin bir örnek verilmiştir: * Örnek 1, #include #include #include void exit_sys(const char *msg); void disp_cap(cap_t caps) { char *captext; if ((captext = cap_to_text(caps, NULL)) == NULL) { cap_free(caps); exit_sys("cap_to_text"); } printf("%s\n", captext); cap_free(captext); } int main(int argc, char *argv[]) { cap_t caps, newcaps; cap_value_t caparray[] = {CAP_CHOWN, CAP_KILL, CAP_SETUID}; if (argc != 2) { fprintf(stderr, "wrong number of arguments!..\n"); exit(EXIT_FAILURE); } if ((newcaps = cap_init()) == NULL) exit_sys("cap_init"); if (cap_set_flag(newcaps, CAP_PERMITTED, 3, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_flag(newcaps, CAP_EFFECTIVE, 3, caparray, CAP_SET) == -1) { cap_free(newcaps); exit_sys("cap_set_flag"); } if (cap_set_file(argv[1], newcaps) == -1) { cap_free(newcaps); exit_sys("cap_set_proc"); } cap_free(newcaps); if ((caps = cap_get_file(argv[1])) == NULL) { cap_free(newcaps); exit_sys("cap_get_file"); } disp_cap(caps); cap_free(caps); cap_free(newcaps); return 0; } void exit_sys(const char *msg) { perror(msg); exit(EXIT_FAILURE); } Ayrıca açık bir dosya için yenekleri alan ve set eden aşağıdaki fonksiyonlar da bulunmaktadır: >> "cap_set_fd" & "cap_get_fd" : Fonksiyonun protototipi şu şekildedir: #include cap_t cap_get_fd(int fd); int cap_set_fd(int fd, cap_t caps); Pekiyi çeşitli yetenekere sahip bir program dosyası exec yapıldığında ne olur? Bu süreç biraz karşıktır. Normal olarak set-user-id ve set-group-id bayraklarında olduğu gibi prosesin dosyada belirtillen yeteneklere sahip olması gerekir. Ancak sürecin açıklayacağımız bazı ayrıntıları vardır. Öncelikle aşağıdaki gibi bir deneme yapalım. Sistem zamanını öğrenmek ve ayarlamak için kullanılan "date" programını kendi dizinimize kopyalayalım: $ whereis date date: /usr/bin/date /usr/share/man/man1/date.1.gz /usr/share/man/man1/date.1posix.gz $ cp /usr/bin/date date Sistem tarihini değiştirmeye çalışalım: $ ./date -s "2025-01-12" ./date: tarih ayarlanamadı: İşleme izin verilmedi Paz 12 Oca 2025 00:00:00 +03 Görüldüğü gibi bu işlem için "sudo" gerekmektedir. Şimdi de dosyaya sistem tarihini değiştirmek için gereken cap_sys_time yeteneğini iliştirelim: $ sudo setcap cap_sys_time=pe date İşlemimizi doğrulayalım: $ sudo getcap date date cap_sys_time=ep Şimdi sistem tarihini değiştirmeye çalışalım: $ ./date -s "2025-01-12" Paz 12 Oca 2025 00:00:00 +03 Görüldüğü gibi biz programı çalıştırdığımızda artık prosesimiz dosyada belirtilen CAP_SYS_TIME yeteneğini kazanmıştır. Ancak konunun bazı ayrıntıları vardır. İzleyen paragraflarda bu ayrıntılar üzerinde duracağız. Diğer yandan bir thread (proses de diyebiliriz) çalıştırılabilen bir dosyayı exec fonksiyonlarıyla çalıştırdığında thread'in yeetenekleri aaşağıdaki biçimde değişime uğramaktadır (The Linux Programming Interface kitabından alınma): P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset) P'(effective) = F(effective) & P'(permitted) P'(inheritable) = P(inheritable) Burada P prosesin exec öncesindeki yeteneklerini P' exec sonrasındaki yeteneklerini, F dosyanın yeteneklerini, cap_bset ise prosesin çevreleyen yeteneklerini (process bounding capabilitiees) belirtmektedir. Dosyanın etkin yetenekleri eskiden her yetenek için farklı değildi. Bütün yenekler için tek bir bayrak kullanılıyordu. Ancak bir süredir bu durum değiştirilmiştir. Artık her yetenek için ayrı bir etkinlik bayrağı turulmaktadır. Prosesin çevreleyen yetenekleri (bounding capabilites) her yetenek için 0/1 biçiminde bitlerden oluşmaktadır. Genellikle prosesin çeevreleyen yeteneklerinin bütün bitleri 1 durumdadır. Örneğin sıradan bir programın yeeneklerine "proc" dosya sisteminden baktığımızda şunları görmekteyiz: CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: 000001ffffffffff Görüldüğü gibi çevreleyen yeteneklerin tüm bitleri 1 durumundadır. (Yüksek anlamlı 0 olan hex digitlere ilişkin bitlerin zaten kullanılmadığına dikkat ediniz.) Buradaki durumu madde madde şöyle açıklayabiliriz: -> exec yapıldıktan sonra prosesin yeni izin verilen yetenekleri şu biçimde oluşturulmaktadır: P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset) Burada bit OR operatörünün solundaki ifade şu anlama gelmektedir: "Prosesin exec öncesindeki aktarılan yeteenekleri eğer aynı zamanda dosyanın aktarılan yetenekleri içerisinde varsa bu yetenekler alınmaktadır". Bit Or operatörünün sağ tarafındaki ifade ise şu anlama gelmektedir: "Dosyanın izin verilen yetenekleri eğer prosesin çavreleyen yeteneklerinde varsa bu yetenekler alınmaktadır. Biz yukarıda prosesin çevreleyen yeteneklerinin genellikle tüm bitlerinin 1 olduğunu beelirtmiştik. Bu durumda dosyanın izin verilen yeteneklerinin hepsi alınacaktır. Örneğin bizim dizine kopyaladığımız "date" programının yetenekleri şöyledir: $ getcap date date cap_sys_time=ep Yani dosyanın etkin ve izin verilen yenekleri "cap_sys_time" yeteeneğie sahiptir. O halde yukarıda satır dikkat alındığında exec sonrasında prosesin izin verilen yeetenekleri "cap_sys_time" içerecektir. -> exec sonrasında prosesin etkin yetenekleri exec sonrasındaki izin verilen yeteneklerinin dosyanın etkin yetenekleriyle maskelenmesi sonucunda elde edilmektedir: P'(effective) = F(effective) & P'(permitted) Dosyanın etkin yeteneklerinin bir şalter görevi gördüğüne dikkat ediniz. Eğer dosyanın etkin yeteneklerindeki bir yetenek 0 ise bu durumda bu yetenek exec sonrasındaki prosesin etkin yeteneklerine yansıtılmayacaktır. Proseesin exec öncesindeki aktarılan yetenekleri ile exec sonrasındaki aktarılan yeetenekleri arasında bir farklılık yoktur: P'(inheritable) = P(inheritable) Pekiyi Linux'taki yetenek konusu neden bu kadar karışık hale getirilmiştir? Tasarımın bu biçimi aslında fazlaca esnek duruma izin vermektedir. Ancak bu esneklikten faydalanma duurmu pratikte çokça karşımıza çıkmamaktadır. Bura aslında temel kullanım için şunlar söylenebilir: -> Bir programı sudo ile çalıştırdığımızda ya da programımızın etkin kullanıcı id'si 0 ise bu durum adeta "tüm etkin yetenekleri set edilmiş" bir proses anlamına gelmektedir. -> Çekirdek içerisinde özel bazı durumlarda genel olarak etkin kullanıcı id'sinin 0 olup olmadığı biçiminde değil thread'in ilgili yeteneğe sahip olup olmadığı biçiminde kontroller yapılmaktadır. -> Biz çalıştırılabilen bir dosyanın beelli bir etkin yeteneğini ve izin verilen yeeteneğini set edersek o programı exec ile çalıştırrdığımızda o yeneke exec sonrasında prosesimizin izin verilen yeteneklerine ve etkin yetenelerine otomatik bir biçimde geçer. Tersten gidersek biz bir programın çalıştırılması ile ilgili prosesin bir yeetenek kazanmasını istiyorsak program dosyası için ilgili yeteneği etkin ve izin verilen biçimde set etmeeliyiz.