এই পর্বে যা শিখবেন
- Protection আর security-র পার্থক্য, protection-এর লক্ষ্য, আর Saltzer–Schroeder-এর নকশার নীতিগুলো — বাস্তব উদাহরণসহ
- Protection domain, static ও dynamic association, Unix-এ user ID আর setuid দিয়ে domain বদল, Multics-এর ring
- Access matrix — copy, owner, control আর switch অধিকার; হাতে-কলমে “switch করে কি পারবে?” ধরনের প্রশ্নের সমাধান, আর একটি simulator
- বাস্তবায়ন: global table, ACL (Unix bit, POSIX ACL, Windows DACL/ACE), capability list (file descriptor, Capsicum, seL4), lock–key; Linux capabilities-এর সাথে পার্থক্য
- ACL বনাম capability — delegation, confused deputy, revocation-এর চার ধরন আর তার কৌশল; RBAC, DAC বনাম MAC আর language-based protection-এর ভূমিকা
১ · Protection বনাম security — ভেতরের নিয়ম আর বাইরের হুমকি
একটি কম্পিউটারে একসাথে বহু process চলে, বহু মানুষ লগইন করেন, আর সবাই একই ডিস্ক, একই মেমরি, একই নেটওয়ার্ক ভাগ করে নেন। আপনার browser যেন আপনার ব্যাংকিং অ্যাপের মেমরি পড়তে না পারে; একজন ছাত্রের প্রোগ্রাম যেন অন্য ছাত্রের ফাইল মুছতে না পারে; একটি বাগওয়ালা driver যেন পুরো সিস্টেম নষ্ট না করে। এই নিয়মগুলো যে ব্যবস্থা ঠিক করে আর প্রয়োগ করে, তার নাম protection.
একটি অফিস-ভবন ভাবুন। ভেতরে প্রতিটি কক্ষে তালা, প্রতিটি কর্মচারীর হাতে নির্দিষ্ট চাবি, হিসাব-শাখার ফাইল ক্যাবিনেট শুধু হিসাবরক্ষকের জন্য — এটি protection: ভেতরে কে কী ছুঁতে পারবে, তার ব্যবস্থা। আর গেটের দারোয়ান, CCTV, ভুয়া পরিচয়পত্র চেনা, রাতে দেয়াল টপকে আসা চোর ঠেকানো — এগুলো security: বাইরের হুমকি আর ভুল মানুষকে সামলানো। ভালো তালা ছাড়া দারোয়ান অর্থহীন, আবার দারোয়ান ঘুমালে ভালো তালাও একসময় ভাঙে।
| দিক | Protection | Security |
|---|---|---|
| প্রশ্ন | যে process চলছে, সে এই object-এ এই কাজ করতে পারবে কি? | যে চালাচ্ছে সে কি সত্যিই সেই মানুষ? সিস্টেম কি আক্রমণ সইতে পারবে? |
| হুমকি | ভেতরের — বাগওয়ালা বা অতিলোভী প্রোগ্রাম, ভুল করে অন্যের ডেটা ছোঁয়া | বাইরের — চুরি করা পাসওয়ার্ড, malware, নেটওয়ার্ক আক্রমণ, social engineering |
| উপকরণ | domain, access matrix, ACL, capability, memory protection, protection ring | authentication, encryption, auditing, update, প্রশিক্ষণ, ভৌত নিরাপত্তা |
সীমারেখাটি পরিষ্কার নয়, আর পাঠ্যবইভেদে আলাদা। তবে মনে রাখার সহজ উপায়: protection হলো mechanism — নিয়ম প্রয়োগের যন্ত্র; security হলো সেই যন্ত্র ব্যবহার করে বাস্তব হুমকির বিরুদ্ধে পুরো সিস্টেমকে টিকিয়ে রাখার নীতি ও চর্চা। পর্ব ৬-এ দেখেছিলেন policy আর mechanism আলাদা রাখার নীতি — protection-এ সেটিই কেন্দ্রীয়: OS দেয় নমনীয় mechanism, আর কোন ফাইল কে পড়বে সেই policy ঠিক করেন ব্যবহারকারী, admin বা প্রতিষ্ঠান।
Protection-এর লক্ষ্য
- ভুল থেকে বাঁচানো: বেশিরভাগ ক্ষতি আসে বাগ থেকে, ষড়যন্ত্র থেকে নয়। একটি ভুল pointer যেন শুধু নিজের process-কে নষ্ট করে — পর্ব ৩০-এ দেখা base/limit register বা পর্ব ৩২-এর page table-এর protection bit এই কাজই করে।
- নীতি প্রয়োগ: “হিসাব-শাখার ফাইল শুধু হিসাব-শাখা পড়বে” — এমন নিয়ম লেখা যায় আর OS প্রতিবার তা প্রয়োগ করে।
- ক্ষতি সীমিত রাখা: একটি অংশ ভাঙলে ক্ষতি যেন সেখানেই থামে — একটি আক্রান্ত web server যেন পুরো মেশিনের মালিক না হয়।
২ · নকশার নীতি — least privilege থেকে defense in depth
১৯৭৫ সালে MIT-র Jerome Saltzer আর Michael Schroeder Proceedings of the IEEE-এ প্রকাশ করেন “The Protection of Information in Computer Systems”. Multics প্রকল্পের অভিজ্ঞতা থেকে লেখা এই গবেষণাপত্রের আটটি নকশা-নীতি আজও OS, browser, cloud আর smartphone-এর নিরাপত্তা-নকশার ভাষা। নিচের টেবিলে সেই আটটি (প্রথম আট সারি), আর তার পরে আরও তিনটি প্রচলিত নীতি, যেগুলো অন্য উৎস থেকে এসেছে:
| নীতি | মানে | OS-এ উদাহরণ |
|---|---|---|
| Economy of mechanism | সুরক্ষার ব্যবস্থা যত ছোট ও সরল, যাচাই তত সহজ | ছোট TCB, microkernel (পর্ব ৬) |
| Fail-safe defaults | অনুমতি দেওয়ার ভিত্তি হবে স্পষ্ট নিয়ম; নিয়ম না থাকলে উত্তর “না” | Windows ACL-এ মিলে যাওয়া কোনো entry না থাকলে access বাতিল; firewall-এর “default deny” |
| Complete mediation | প্রতিটি object-এর প্রতিটি access যাচাই করতে হবে, পুরোনো উত্তর মনে রেখে নয় | প্রতিটি system call-এ kernel পরীক্ষা করে; তবে fd-তে যাচাই হয় open-এর সময়ে (অংশ ৭) |
| Open design | নকশা গোপন রেখে নিরাপত্তা নয়; গোপন থাকবে শুধু key বা পাসওয়ার্ড | প্রকাশ্য, পর্যালোচিত encryption algorithm; Linux-এর খোলা কোড |
| Separation of privilege | গুরুত্বপূর্ণ কাজে একটির বেশি শর্ত বা চাবি | পাসওয়ার্ড + OTP; ব্যাংকের লকার খুলতে দুই চাবি |
| Least privilege | প্রতিটি প্রোগ্রাম আর ব্যবহারকারী শুধু কাজের জন্য দরকারি ন্যূনতম অধিকার পাবে | web server port 80 খুলে root অধিকার ছেড়ে দেয়; admin-এর আলাদা দৈনন্দিন অ্যাকাউন্ট |
| Least common mechanism | ব্যবহারকারীদের মধ্যে ভাগ করা ব্যবস্থা যত কম, তথ্য ফাঁসের পথ তত কম | /tmp-এর মতো সবার ভাগের ডিরেক্টরি ঝুঁকিপূর্ণ, তাই sticky bit ও আলাদা tmp |
| Psychological acceptability | নিয়ম ব্যবহারে সহজ না হলে মানুষ এড়িয়ে যাবেন | অতিরিক্ত পপ-আপে মানুষ না পড়েই “Allow” চাপেন |
| Need-to-know | তথ্য পাবেন শুধু যাঁর কাজের জন্য সেটি জানা দরকার — least privilege-এর তথ্য-সংস্করণ | হাসপাতালের নার্স শুধু নিজের ওয়ার্ডের রোগীর রেকর্ড দেখেন |
| Compartmentalisation | সিস্টেমকে আলাদা কক্ষে ভাগ, যাতে একটি কক্ষ ভাঙলে অন্যগুলো টেকে | browser-এর প্রতিটি ট্যাব আলাদা process আর sandbox-এ |
| Defense in depth | একাধিক স্বাধীন স্তরের সুরক্ষা, যাতে একটি ব্যর্থ হলে পরেরটি ঠেকায় | file permission + MAC + seccomp + ASLR একসাথে (পর্ব ৪৮–৪৯) |
উৎস নিয়ে সতর্কতা: Saltzer ও Schroeder-এর তালিকায় আটটি নীতি (ওপরের প্রথম আটটি); “work factor” আর “compromise recording”-কে তাঁরা আলাদাভাবে, কম প্রযোজ্য বলে উল্লেখ করেছিলেন। “Need-to-know” আর “defense in depth” এসেছে সামরিক চর্চা থেকে। পরীক্ষায় সবগুলোকেই সাধারণত “protection-এর নীতি” বলা হয় — উৎস ঠিক রাখলে উত্তর আরও ভালো।
Least privilege — সবচেয়ে বেশি ব্যবহৃত নীতি
একটি ছোট উদাহরণে নীতিটির শক্তি দেখুন। একটি web server-কে port 80 বা 443-এ শুনতে হয়; Unix-এ ১০২৪-এর নিচের port খুলতে বিশেষ অধিকার লাগে। পুরোনো সহজ উপায়: পুরো server root হিসেবে চালানো। তখন server-এর একটি বাগ মানে আক্রমণকারীর হাতে পুরো মেশিন। Least privilege-এর উপায় তিনটি: (১) root হিসেবে শুরু করে port খুলেই সাধারণ user-এ নেমে যাওয়া — nginx আর Apache-এর মূল process এভাবেই worker-দের সাধারণ user-এ চালায়; (২) শুধু “নিচের port খোলার” অধিকারটি দেওয়া — Linux-এর CAP_NET_BIND_SERVICE (অংশ ৭); (৩) systemd-এর socket activation-এ port খোলে systemd, আর server হাতে পায় শুধু একটি খোলা socket — অর্থাৎ একটি capability.
৩ · Protection domain — কে, কী, কতটা
Protection-এর ভাষায় সিস্টেম দুই রকম জিনিস দিয়ে তৈরি। Object — যাকে রক্ষা করতে হবে: hardware object (CPU, মেমরির অংশ, প্রিন্টার, ডিস্ক) আর software object (ফাইল, প্রোগ্রাম, semaphore, socket). প্রতিটি object-এর একটি অনন্য নাম আর কিছু নির্দিষ্ট কাজ আছে — ফাইলে read, write, execute; প্রিন্টারে print; semaphore-এ wait আর signal. আর subject — যে কাজ করতে চায়: সাধারণত একটি process.
একটি access right হলো একটি জোড়া <object, rights-set> — যেমন <F1, {read, write}>. একটি protection domain হলো এমন জোড়ার একটি সেট: D = { <F1, {read}>, <printer, {print}> }. প্রতিটি process কোনো একটি domain-এর ভেতরে চলে, আর শুধু সেই domain-এর access right ব্যবহার করতে পারে। দুটি domain-এর সেট ভাগাভাগিও করতে পারে — একই <printer, {print}> দুটিতেই থাকতে পারে।
Domain হতে পারে একজন ব্যবহারকারী (Unix-এর user ID), একটি process (seL4-এ process-এর হাতের capability-গুলোই তার domain), বা একটি procedure (language-based protection, অংশ ৯)।
Need-to-know মানতে হলে — static বনাম dynamic association
- Static association: একটি process-এর domain তার পুরো জীবন ধরে একই। সমস্যা: একটি process-এর কাজের বিভিন্ন ধাপে বিভিন্ন অধিকার দরকার — শুরুতে একটি config ফাইল পড়া, পরে শুধু লগ লেখা। Domain স্থির হলে সব ধাপের অধিকার শুরু থেকেই দিতে হয়, যা need-to-know ভাঙে। তাই static ব্যবস্থায় domain-এর ভেতরের অধিকার বদলানোর একটি উপায় লাগে।
- Dynamic association: process চলার মাঝে এক domain থেকে আরেকটিতে যেতে পারে — domain switching. কোন domain থেকে কোনটিতে যাওয়া যাবে, সেটিও একটি নিয়ন্ত্রিত অধিকার (অংশ ৪-এর switch).
আগের পর্বগুলোর সাথে যোগ — ring আর dual mode
সবচেয়ে মৌলিক domain-ভাগ আপনি আগেই দেখেছেন: পর্ব ৪-এর protection ring আর পর্ব ৫-এর user mode/kernel mode. সেখানে domain মাত্র দুটি, আর domain switch-এর একমাত্র বৈধ দরজা system call (বা interrupt/exception) — process নিজের ইচ্ছামতো kernel-এর যেকোনো জায়গায় লাফ দিতে পারে না, শুধু নির্দিষ্ট প্রবেশপথে ঢোকে। এই পর্বের domain সেই ধারণারই সূক্ষ্ম রূপ: user mode-এর ভেতরেই হাজার রকম domain.
Unix-এ domain = user ID, আর setuid = domain switch
Unix-এ একটি process-এর domain মোটামুটি তার effective user ID (আর group ID-গুলো) দিয়ে ঠিক হয়। Domain বদলের চেনা পথ setuid: কোনো executable ফাইলের setuid bit চালু থাকলে, যে-ই সেটি exec করুক, নতুন প্রোগ্রাম চলে ফাইলের মালিকের effective UID নিয়ে। যেমন passwd প্রোগ্রাম সাধারণত root-এর মালিকানায় setuid — সাধারণ ব্যবহারকারী এটি চালালে process সাময়িকভাবে root-এর domain-এ যায়, যাতে শুধু পাসওয়ার্ড-ফাইলে নিজের লাইনটি বদলাতে পারে। এটি access matrix-এর ভাষায় একটি “switch” অধিকার — সব ব্যবহারকারীর domain থেকে একটি নির্দিষ্ট, সতর্কভাবে লেখা প্রোগ্রামের মাধ্যমে root-এর domain-এ ঢোকার দরজা। Setuid, sudo, real/effective/saved UID আর তার ঝুঁকিগুলো বিস্তারিত আছে পর্ব ৪৭-এ।
Multics-এর ring: ১৯৬০-এর দশকের MULTICS প্রকল্পে domain সাজানো হয়েছিল এককেন্দ্রিক বৃত্তের মতো — ring 0 (সবচেয়ে বেশি অধিকার) থেকে ring 7. Ring i-তে থাকা process ring j-এর (j ≥ i) সব অধিকার পায়, উল্টোটা নয়। কম অধিকারের ring থেকে বেশি অধিকারের ring-এ যাওয়া যেত শুধু নির্দিষ্ট “gate”-এর মাধ্যমে, যা যুক্তিগুলো যাচাই করত। ধারণাটির বর্ণনা আছে Robert Graham-এর ১৯৬৮ সালের গবেষণাপত্রে; পরের Honeywell 6180-এ ring হার্ডওয়্যারেই প্রয়োগ হতো। দুর্বলতা: ring স্তরবিন্যস্ত, তাই “A শুধু X পড়বে, B শুধু Y” — এমন পাশাপাশি domain বানানো যায় না, need-to-know মানা যায় না। তাই আরও সাধারণ মডেল দরকার হলো।
৪ · Access matrix — এক টেবিলে পুরো নিয়ম
১৯৭১ সালে Butler Lampson তাঁর “Protection” নামের গবেষণাপত্রে protection-এর একটি সাধারণ মডেল দেন, যা পরে access matrix নামে পরিচিত হয়; ১৯৭২ সালে G. Scott Graham আর Peter Denning মডেলটিকে আরও বিস্তারিত করেন — কোন অধিকার কে কীভাবে দিতে বা কেড়ে নিতে পারবে, তার নিয়মসহ। (মডেলের নামকরণ আর খুঁটিনাটির কৃতিত্ব বিভিন্ন বইয়ে একটু ভিন্নভাবে দেওয়া হয়; মূল ধারণা এই দুই কাজেরই।)
সারি = domain, কলাম = object, ঘর access(i, j) = domain Di-এ চলা process object Oj-এ কোন কাজগুলো করতে পারবে। Process Di-এ থেকে Oj-এ কাজ “op” করতে চাইলে OS শুধু দেখে op ∈ access(i, j) কি না। Matrix হলো policy; প্রতিটি অনুরোধে ঘর মিলিয়ে দেখা হলো mechanism.
বিশেষ অধিকার — matrix নিজেকে কীভাবে বদলায়
কে matrix বদলাতে পারবে, তাও matrix-এর ভেতরেই লেখা থাকে চারটি বিশেষ অধিকারে:
- Copy (*): ঘরে
r*থাকলে domain শুধু পড়তেই পারে না, “r” অধিকারটি একই কলামের অন্য কোনো সারিতে (অন্য domain-কে) দিতে পারে। প্রকারভেদ: transfer (দিলে নিজের থেকে মুছে যায়) আর limited copy (দেওয়া কপিতে * থাকে না, তাই সে আর এগিয়ে দিতে পারে না)। - Owner: কোনো ঘরে “owner” থাকলে সেই domain ওই কলামের যেকোনো ঘরে অধিকার যোগ বা মুছতে পারে — ফাইলের মালিক যেমন
chmodদিয়ে অন্যদের অধিকার বদলান। - Control: শুধু domain-object-এ প্রযোজ্য। access(i, j)-তে “control” থাকলে Di সারি Dj-এর যেকোনো অধিকার মুছতে পারে — একজন প্রধান যেমন নিজের অধীন বিভাগের অধিকার নিয়ন্ত্রণ করেন। Copy আর owner বদলায় কলাম, control বদলায় সারি.
- Switch: domain-কেও কলাম হিসেবে রাখা হয়; access(i, j)-তে “switch” থাকলে Di-এর process Dj-তে চলে যেতে পারে। এভাবে dynamic association-ও একই টেবিলে।
Matrix-কে নিজেই নিজের অধিকারে রক্ষা করা — এটিই মডেলের সৌন্দর্য। তবে এর একটি গভীর সীমা আছে: অধিকারের নিয়ম যথেষ্ট সাধারণ হলে “এই অধিকার কি কখনো কোনো অবাঞ্ছিত domain-এ পৌঁছাতে পারে?” প্রশ্নটির সাধারণ কোনো algorithm নেই (ভাবনার প্রশ্ন ৪)।
একটি পূর্ণ উদাহরণ — চারটি domain, তিনটি ফাইল, একটি প্রিন্টার
এই পর্বের বাকি অংশে এই matrix-টিই ব্যবহার হবে। সংকেত: r = read, w = write, p = print, o = owner, s = switch, c = control, * = copy.
| domain ↓ / object → | F1 | F2 | F3 | P (প্রিন্টার) | D1 | D2 | D3 | D4 |
|---|---|---|---|---|---|---|---|---|
| D1 | r | r* | – | – | – | s | – | – |
| D2 | – | r | – | p | – | – | s | – |
| D3 | r | – | o, r, w | – | – | – | – | – |
| D4 | o, r, w | – | r | – | s | – | c | – |
Switch-গুলো একটি দিকযুক্ত graph বানায়: D4 → D1 → D2 → D3, আর D3 থেকে কোথাও যাওয়া যায় না। “Dx কি (switch করে) O-তে op করতে পারে?” প্রশ্নের উত্তর তাই একটি graph-এর reachability প্রশ্ন: Dx থেকে switch-পথে পৌঁছানো যায় এমন কোনো domain-এর O-কলামে op আছে কি না। নিচের stepper চারটি প্রশ্ন ধাপে ধাপে মেলায়।
1 ask: D1 read F3 ? check row D12 switch D1 → D2, check row D23 switch D2 → D3, check row D34 ask: D3 print P ?5 ask: D4 print P ?6 ask: D1 write F1 ?
| যে domain দেখা হচ্ছে | |
| লক্ষ্য-ঘর | |
| পরের switch | |
| ফল |
- প্রশ্ন ১, শুরু: D1-এ চলা একটি process F3 পড়তে চায়। সারি D1-এ F3-এর ঘর খালি — সরাসরি অনুমতি নেই। কিন্তু (D1, D2) ঘরে s আছে, তাই খোঁজা চলবে।
- D2-তে switch: সারি D2-তেও F3-এর ঘর খালি। তবে (D2, D3) ঘরে s — আরও এক ধাপ।
- D3-তে switch: (D3, F3) ঘরে o, r, w — read আছে। উত্তর: হ্যাঁ, D1 → D2 → D3 পথে। পরীক্ষার খাতায় পুরো পথটিই লিখুন।
- প্রশ্ন ২ — D3 কি প্রিন্ট করতে পারে? (D3, P) খালি, আর সারি D3-এ কোনো s নেই। Switch এক-দিকের: D2 থেকে D3-তে আসা যায়, কিন্তু D3 থেকে D2-তে ফেরার অধিকার নেই। উত্তর: না।
- প্রশ্ন ৩ — D4 কি প্রিন্ট করতে পারে? সারি D4-এ P নেই, কিন্তু (D4, D1) = s; D1-এ P নেই, (D1, D2) = s; (D2, P) = p. উত্তর: হ্যাঁ, D4 → D1 → D2 পথে।
- প্রশ্ন ৪ — D1 কি F1-এ লিখতে পারে? D1 থেকে পৌঁছানো যায় D1, D2, D3 — এদের F1-ঘরে r, –, r; কোথাও w নেই। F1-এ লেখার অধিকার শুধু D4-এর, আর D1 থেকে D4-এ কোনো পথ নেই। উত্তর: না।
নিচের simulator একই matrix-এ breadth-first search চালিয়ে প্রশ্নগুলোর উত্তর আর সবচেয়ে ছোট switch-পথ দেয়, তারপর copy অধিকারের একটি প্রয়োগ দেখায়:
// Access matrix, domains as objects. Rows = domains, columns = objects.
// r read, w write, p print, o owner, s switch (into that domain), c control, * copy flag
const M = {
D1: { F1: "r", F2: "r*", D2: "s" },
D2: { F2: "r", P: "p", D3: "s" },
D3: { F1: "r", F3: "orw" },
D4: { F1: "orw", F3: "r", D1: "s", D3: "c" },
};
const cols = ["F1", "F2", "F3", "P", "D1", "D2", "D3", "D4"];
const has = (d, o, r) => (M[d][o] || "").includes(r);
const word = { r: "read", w: "write", p: "print" };
console.log(" " + cols.map(c => c.padEnd(5)).join(""));
for (const d in M) console.log(d.padEnd(4) + cols.map(c => (M[d][c] || "-").padEnd(5)).join(""));
function query(start, right, obj) { // breadth-first search over switch rights
const prev = { [start]: null }, queue = [start];
while (queue.length) {
const d = queue.shift();
if (has(d, obj, right)) {
const path = [];
for (let x = d; x; x = prev[x]) path.unshift(x);
return "GRANTED via " + path.join(" -> ");
}
for (const n in M) if (has(d, n, "s") && !(n in prev)) { prev[n] = d; queue.push(n); }
}
return "DENIED (no reachable domain has it)";
}
const asks = [["D1", "r", "F3"], ["D1", "w", "F1"], ["D2", "p", "P"],
["D3", "p", "P"], ["D4", "p", "P"], ["D3", "r", "F2"]];
console.log("\nquery result");
for (const [d, r, o] of asks) console.log(`${d} ${word[r]} ${o}?`.padEnd(15) + query(d, r, o));
// copy right: D1 holds r* on F2, so it may put "r" for F2 into another row
if (has("D1", "F2", "r*")) M.D3.F2 = "r";
console.log("\nafter D1 copies r(F2) to D3:\n" + "D3 read F2?".padEnd(15) + query("D3", "r", "F2"));আউটপুট (নির্ধারিত, প্রতিবার একই):
F1 F2 F3 P D1 D2 D3 D4
D1 r r* - - - s - -
D2 - r - p - - s -
D3 r - orw - - - - -
D4 orw - r - s - c -
query result
D1 read F3? GRANTED via D1 -> D2 -> D3
D1 write F1? DENIED (no reachable domain has it)
D2 print P? GRANTED via D2
D3 print P? DENIED (no reachable domain has it)
D4 print P? GRANTED via D4 -> D1 -> D2
D3 read F2? DENIED (no reachable domain has it)
after D1 copies r(F2) to D3:
D3 read F2? GRANTED via D3
প্রথম টেবিলটি ওপরের matrix-এরই ছাপা রূপ। query() ফাংশন শুরুর domain থেকে queue ধরে এগোয়: প্রথমে নিজের সারিতে অধিকার খোঁজে, না পেলে সারির প্রতিটি s ধরে পরের domain-কে queue-তে রাখে, আর prev-এ মনে রাখে কোথা থেকে এসেছে — তাই GRANTED via D4 -> D1 -> D2-এর মতো পুরো পথ ছাপা যায়। D3 read F2? প্রথমবার DENIED; তারপর D1 তার r* ব্যবহার করে F2-কলামে D3-এর ঘরে “r” বসাল (copy শুধু একই কলামে কাজ করে), আর এবার GRANTED via D3. D3 পেল শুধু r, r* নয় — limited copy, তাই D3 এটি আর এগিয়ে দিতে পারবে না। asks-এ নিজের প্রশ্ন যোগ করে দেখুন।
সাবধান — switch মানে দুই domain-এর অধিকার একসাথে নয়। D1 থেকে D2-তে গেলে process তখন শুধু D2-এর অধিকার পায়, D1-এরগুলো সাথে নেয় না। তাই প্রতিটি কাজ হয় সেই domain-এ, যেখানে তার অধিকার আছে। বাস্তবে অবশ্য switch-এর আগে খোলা fd process-এর হাতে থেকে যায় — অংশ ৭-এ দেখবেন কেন।
৫ · Matrix বাস্তবে কীভাবে রাখা হয় — global table, ACL, capability আর lock–key
বাস্তব সিস্টেমে matrix বিশাল আর বেশিরভাগ ঘর খালি (sparse), তাই পুরো টেবিল রাখা অসম্ভব। চারটি প্রচলিত উপায়:
- Global table: শুধু ভরা ঘরগুলোর <domain, object, rights-set> ত্রয়ীর তালিকা। সরল, কিন্তু বিশাল, আর “সবাই পড়বে” ধরনের নিয়মে প্রতিটি domain-এর আলাদা entry লাগে।
- Access control list (ACL): matrix-কে কলাম ধরে কাটা। প্রতিটি object-এর সাথে একটি তালিকা: <domain, rights-set>, খালি ঘর বাদ। সাথে একটি ডিফল্ট সেট রাখা যায় (“বাকি সবার জন্য”)। ব্যবহারকারীর চিন্তার সাথে মেলে — “আমার এই ফাইল কে কে পড়বে?”
- Capability list (C-list): matrix-কে সারি ধরে কাটা। প্রতিটি domain-এর হাতে একটি তালিকা: <object, rights-set>. তালিকার প্রতিটি উপাদান একটি capability — object-এর নাম আর অধিকার একসাথে বাঁধা একটি টোকেন। Process শুধু টোকেন দেখিয়ে কাজ চায়; টোকেন থাকা মানেই অনুমতি।
- Lock–key: মাঝামাঝি পথ। প্রতিটি object-এর একটি lock-এর তালিকা (অনন্য বিট-প্যাটার্ন), প্রতিটি domain-এর একটি key-এর তালিকা। Domain-এর কোনো key object-এর কোনো lock-এর সাথে মিললে access. Key-গুলো OS-এর হাতে থাকে, process নিজে বদলাতে পারে না; আর একটি lock মুছে দিলে সেই key-ধারী সবার অধিকার একসাথে বাতিল।
নিচের stepper একই matrix থেকে দুই দৃশ্য বের করে, আর দেখায় কোন প্রশ্নের উত্তর কোন দৃশ্যে সহজ।
1 full access matrix2 cut column F1 → ACL(F1)3 cut column F3 → ACL(F3)4 cut row D1 → C-list(D1)5 cut row D4 → C-list(D4)6 revoke D1's read on F1
| কী দেখছি | |
| তালিকা | |
| সহজে উত্তর দেয় |
- পুরো matrix: ৪টি domain × ৮টি object = ৩২টি ঘর, যার ১২টি ভরা আর ২০টি খালি। বাস্তবে খালির অনুপাত অনেক বেশি — তাই খালি ঘর বাদ দিয়ে রাখতে হয়।
- কলাম F1 কাটা: ACL(F1) = (D1, r), (D3, r), (D4, o r w). তালিকাটি ফাইলের সাথে থাকে — Unix-এ inode-এ, Windows-এ ফাইলের security descriptor-এ। “F1 কে কে পড়তে পারে” — একটি তালিকা পড়লেই উত্তর।
- কলাম F3 কাটা: ACL(F3) = (D3, o r w), (D4, r). D3-এর হাতে owner, তাই D3 এই তালিকা বদলাতে পারে — ঠিক যেমন ফাইলের মালিক chmod চালান।
- সারি D1 কাটা: C-list(D1) = (F1, r), (F2, r*), (D2, s). এই তালিকা domain-এর সাথে থাকে, আর kernel এটি এমনভাবে রাখে যাতে process নিজে কোনো টোকেন বানাতে বা বদলাতে না পারে।
- সারি D4 কাটা: C-list(D4) = (F1, o r w), (F3, r), (D1, s), (D3, c). “D4 কী কী পারে” — এক তালিকায় উত্তর; কিন্তু “F1 কে কে পড়ে” জানতে সব domain-এর তালিকা ঘাঁটতে হয়।
- অধিকার কেড়ে নেওয়া: D1-এর F1-পড়ার অধিকার বাতিল করতে ACL-এ শুধু F1-এর তালিকা থেকে একটি entry মুছলেই হয়। Capability-তে টোকেনটি D1-এর হাতে, আর D1 হয়তো এর কপি অন্যদেরও দিয়েছে — সবগুলো খুঁজে বাতিল করা কঠিন (অংশ ৮)।
বেশিরভাগ বাস্তব OS দুটিকে মেলায়: প্রথমবার access-এর সময় ACL যাচাই হয়, আর সফল হলে process একটি capability হাতে পায়, যা দিয়ে পরের সব কাজ চলে। Unix-এর open() ঠিক তাই — ACL (permission bit) যাচাই করে একটি file descriptor দেয়; Windows-এর CreateFile() DACL যাচাই করে একটি handle দেয়। পরের দুই অংশে দুটি দিক আলাদা করে দেখা যাক।
৬ · ACL বাস্তবে — Unix-এর ৯টি বিট, POSIX ACL আর Windows DACL
Unix-এর owner/group/other — সংকুচিত ACL
পর্ব ৩ আর ৪১-এ দেখা rwx permission আসলে একটি খুব ছোট, নির্দিষ্ট মাপের ACL: ঠিক তিনটি entry — মালিক, ফাইলের group, আর বাকি সবাই — প্রতিটিতে r, w, x. পুরো ACL-এর বদলে ৯টি বিট (আর setuid, setgid, sticky মিলিয়ে ১২টি) inode-এ ধরে যায়, যাচাইও দ্রুত। দাম: “শুধু রহিম আর করিম পড়বে, বাকি group নয়” — এমন নিয়ম তিন entry-তে লেখা যায় না, admin-কে নতুন group বানাতে হয়।
Kernel-এর নিয়মও ACL-এর মতো ক্রমানুসারী: process-এর effective UID ফাইলের মালিক হলে শুধু owner-বিট দেখা হয়; না হলে process-এর কোনো group ফাইলের group হলে শুধু group-বিট; না হলে other-বিট। তাই ----rw-rw- ফাইলের মালিক নিজে পড়তে পারেন না, যদিও “অন্যরা” পারে — owner entry মিলে গেছে, সেখানে অধিকার নেই, তাই উত্তর “না” (ব্যতিক্রম: root, যার জন্য Linux-এ CAP_DAC_OVERRIDE এই যাচাই এড়িয়ে যায়)। নিচের bash block এই ৯টি বিটকে একটি তিন-entry-র তালিকা হিসেবে পড়ে, আর getfacl/setfacl থাকলে বাড়তি ACL-ও দেখায়:
# Unix permission bits = a tiny, fixed-size ACL: owner, group, other
umask 022
echo "salary sheet" > report.txt
chmod 640 report.txt # owner rw-, group r--, other ---
ls -l report.txt
stat -c 'mode=%A octal=%a owner=%U group=%G' report.txt
p=$(stat -c %A report.txt) # e.g. -rw-r-----
echo "--- the same 9 bits read as an access list ---"
u=$(stat -c %U report.txt); g=$(stat -c %G report.txt)
printf 'ACE 1 %-22s : %s\n' "user $u (owner)" "${p:1:3}"
printf 'ACE 2 %-22s : %s\n' "group $g" "${p:4:3}"
printf 'ACE 3 %-22s : %s\n' "other (everyone else)" "${p:7:3}"
chmod o+r report.txt # edit one entry of the list
echo "after chmod o+r -> $(stat -c %A report.txt)"
if command -v getfacl >/dev/null && command -v setfacl >/dev/null; then
if setfacl -m u:nobody:r report.txt 2>/dev/null; then
getfacl -p report.txt
ls -l report.txt # a '+' after the mode = extended ACL
else
echo "setfacl exists, but this file system does not accept ACLs"
fi
else
echo "getfacl/setfacl are not installed here (package 'acl' on Linux)"
fi
rm -f report.txtআউটপুট (তারিখ-সময় আপনার চালানোর মুহূর্তের হবে):
-rw-r----- 1 wandbox wandbox 13 Oct 25 20:11 report.txt
mode=-rw-r----- octal=640 owner=wandbox group=wandbox
--- the same 9 bits read as an access list ---
ACE 1 user wandbox (owner) : rw-
ACE 2 group wandbox : r--
ACE 3 other (everyone else) : ---
after chmod o+r -> -rw-r--r--
getfacl/setfacl are not installed here (package 'acl' on Linux)
-rw-r------এর প্রথম অক্ষর ফাইলের ধরন, তারপর তিন অক্ষরের তিনটি দল — ঠিক তিনটি ACE (access control entry). stat -c %a একই তথ্য octal-এ দেয়: 640 = 6 (r+w = 4+2) মালিকের, 4 (r) group-এর, 0 বাকিদের। chmod o+r তালিকার তৃতীয় entry বদলাল — owner অধিকারের প্রয়োগ। এই runner-এ getfacl/setfacl ইনস্টল নেই, তাই শেষ লাইনটি এসেছে; স্ক্রিপ্টটি সেটি আগে পরীক্ষা করে নেয় বলে ভাঙে না। পূর্ণ Linux-এ (Debian/Ubuntu-তে acl প্যাকেজ) নিচের নমুনার মতো দেখায়।
POSIX ACL — নাম ধরে বাড়তি entry
তিন entry-র সীমা পেরোতে বহু Unix-ধরনের সিস্টেম POSIX 1003.1e-এর খসড়া থেকে আসা ACL সমর্থন করে (মানটি ১৯৯০-এর দশকের শেষে প্রত্যাহার হলেও খসড়ার নকশাই বাস্তবে চলছে)। Linux-এ ext4, XFS, Btrfs-এর মতো file system-এ এটি পাওয়া যায়। নমুনা (পূর্ণ Linux-এ, ব্যবহারকারী rahim আর group accounts আছে ধরে নিয়ে; এখানে চালানো যায় না):
$ setfacl -m u:rahim:r report.txt # রহিমকে শুধু পড়ার entry
$ setfacl -m g:accounts:rw report.txt # accounts group-কে পড়া-লেখা
$ getfacl report.txt
# file: report.txt
# owner: karim
# group: staff
user::rw-
user:rahim:r--
group::r--
group:accounts:rw-
mask::rw-
other::---
$ ls -l report.txt
-rw-rw----+ 1 karim staff 13 Oct 26 10:00 report.txt
$ setfacl -x u:rahim report.txt # রহিমের entry মুছে ফেলা (revoke)
$ setfacl -b report.txt # সব বাড়তি entry মুছে আবার ৯টি বিটদুটি জিনিস লক্ষ করুন। ls -l-এ মোডের পরে + মানে “এর বাইরেও ACL আছে” — শুধু ৯টি বিট দেখে সিদ্ধান্ত নেবেন না। আর mask: নামযুক্ত entry আর group entry-র সর্বোচ্চ সীমা; তখন ls -l-এর মাঝের তিনটি বিট আসলে mask দেখায়। ডিরেক্টরিতে “default” ACL রাখলে ভেতরের নতুন ফাইল তা উত্তরাধিকার পায়। macOS আর NFSv4-ধরনের ACL Windows-এর মডেলের কাছাকাছি, কমান্ডও আলাদা (macOS-এ chmod +a, ls -le).
Windows — security descriptor, DACL আর ACE
Windows NT শুরু থেকেই (১৯৯৩) পূর্ণ ACL-এর ওপর দাঁড়িয়ে। প্রতিটি securable object — ফাইল, registry key, process, thread, named pipe — একটি security descriptor রাখে, যাতে থাকে মালিকের SID (security identifier — ব্যবহারকারী বা group-এর অনন্য পরিচয়), একটি DACL (discretionary ACL — কে কী পারবে) আর একটি SACL (কোন access-এর audit log লেখা হবে)। DACL হলো ACE-এর ক্রমিক তালিকা; প্রতিটি ACE “allow” বা “deny”, একটি SID আর একটি access mask (read, write, delete, change permissions…)।
- যাচাইয়ের ক্রম: ACE-গুলো পর্যায়ক্রমে দেখা হয়, যতক্ষণ না চাওয়া সব অধিকার মঞ্জুর হয় বা কোনো deny মেলে; স্বাভাবিক (canonical) ক্রমে স্পষ্ট deny আগে বসে।
- Fail-safe default: কোনো ACE না মিললে access বাতিল; খালি DACL মানে কেউ কিছু পারে না। কিন্তু DACL একদম না থাকলে (NULL DACL) সবাই সব পারে — একটি পুরোনো বিপজ্জনক ফাঁদ।
- Inheritance আর handle: ফোল্ডারের ACE সন্তান-ফাইলে যায়; আর যাচাই হয় খোলার সময়ে, মঞ্জুর হওয়া অধিকার handle-এ জমা থাকে — Unix-এর fd-এর মতো।
PS> icacls C:\Data\report.txt
C:\Data\report.txt OFFICE\karim:(F)
OFFICE\accounts:(R,W)
BUILTIN\Administrators:(I)(F)
NT AUTHORITY\SYSTEM:(I)(F)
PS> icacls C:\Data\report.txt /grant "OFFICE\rahim:(R)" # একটি allow ACE যোগ
PS> icacls C:\Data\report.txt /remove "OFFICE\rahim" # সেই entry মুছে ফেলা
PS> (Get-Acl C:\Data\report.txt).Access | Format-Table IdentityReference, AccessControlType, FileSystemRights(F) = full control, (R,W) = পড়া ও লেখা, (I) = ফোল্ডার থেকে উত্তরাধিকারসূত্রে পাওয়া ACE. Unix-এর তুলনায় Windows-এর ACL অনেক বেশি সূক্ষ্ম, কিন্তু সেই কারণেই ভুল কনফিগারেশনও সহজ — ভাবনার প্রশ্ন ২ দেখুন।
৭ · Capability বাস্তবে — জাল করা যায় না এমন টোকেন
Capability-র ধারণাটি স্পষ্ট করে প্রথম লেখেন Jack Dennis আর Earl Van Horn, ১৯৬৬ সালে (“Programming Semantics for Multiprogrammed Computations”). মূল শর্ত একটিই: capability জাল করা যাবে না। Process যদি নিজে একটি টোকেন বানিয়ে “আমার কাছে F3-এ লেখার অধিকার আছে” বলতে পারে, পুরো ব্যবস্থা অর্থহীন। এটি নিশ্চিত করার তিন পথ:
- Tagged hardware: মেমরির প্রতিটি word-এর সাথে একটি বাড়তি tag বিট — “এটি capability”. সাধারণ instruction tag-যুক্ত word বদলাতে পারে না, শুধু বিশেষ instruction পারে। ইতিহাসে Burroughs-এর B5000 পরিবারের মতো tagged মেশিন, Cambridge-এর CAP কম্পিউটার, IBM System/38 আর Intel iAPX 432 এ ধরনের নকশার উদাহরণ; আধুনিক গবেষণায় CHERI (আরও গভীরে দেখুন)।
- Kernel-এর ভেতরে রাখা: capability-র আসল তালিকা থাকে kernel-এর মেমরিতে; process-এর হাতে থাকে শুধু সেই তালিকার একটি সূচক (index). Process সূচক বানাতে পারে, কিন্তু kernel-এর টেবিলে না থাকা অধিকার বানাতে পারে না। CMU-র Hydra থেকে seL4 পর্যন্ত এবং — চেনা উদাহরণ — Unix-এর file descriptor এই পথে চলে।
- Cryptographic টোকেন: capability-তে একটি গোপন key দিয়ে বানানো চিহ্ন থাকে, বদলালে ধরা পড়ে — যেমন Amoeba OS, বা ক্লাউডের pre-signed URL.
File descriptor — আপনার প্রতিদিনের capability
পর্ব ৪১-এ দেখেছেন, open() একটি ছোট পূর্ণসংখ্যা — file descriptor — ফেরত দেয়, যা process-এর fd টেবিলের একটি সূচক; টেবিলের entry kernel-এর open-file table-এর একটি রেকর্ডকে নির্দেশ করে, যাতে খোলার মোড (read-only, write-only, read-write) লেখা থাকে। অর্থাৎ fd = object (কোন ফাইল) + অধিকার (কোন মোড), kernel-রক্ষিত — capability-র সংজ্ঞাই। আর permission যাচাই হয় শুধু open()-এর সময়ে; পরে read()/write() শুধু fd-এর মোড দেখে। নিচের প্রোগ্রাম এটি সরাসরি দেখায়:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
const char *path = "budget.txt";
FILE *f = fopen(path, "w");
if (!f) { perror("fopen"); return 1; }
fputs("budget 2026: 42 crore\n", f);
fclose(f);
int fd = open(path, O_RDONLY); /* the permission check happens HERE */
if (fd < 0) { perror("open"); return 1; }
printf("1. open(O_RDONLY) granted, fd is %s\n", fd > 2 ? "a small int > 2" : "?");
chmod(path, 0); /* owner now removes ALL permissions */
struct stat st;
stat(path, &st);
printf("2. chmod done, mode is now %03o\n", (unsigned)(st.st_mode & 0777));
int fd2 = open(path, O_RDONLY); /* a NEW open is checked again */
printf("3. new open(): %s\n", fd2 < 0 ? strerror(errno) : "granted");
char buf[64];
ssize_t n = read(fd, buf, sizeof buf - 1); /* the OLD fd still works */
buf[n > 0 ? n : 0] = '\0';
printf("4. read() on the old fd: %zd bytes: %s", n, buf);
ssize_t w = write(fd, "x", 1); /* the fd carries only the read right */
printf("5. write() on the old fd: %s\n", w < 0 ? strerror(errno) : "ok");
close(fd);
unlink(path);
return 0;
}আউটপুট:
1. open(O_RDONLY) granted, fd is a small int > 2
2. chmod done, mode is now 000
3. new open(): Permission denied
4. read() on the old fd: 22 bytes: budget 2026: 42 crore
5. write() on the old fd: Bad file descriptor
ধাপ ১-এ open() permission-বিট যাচাই করে fd দিল। ধাপ ২-এ মালিক chmod(path, 0) দিয়ে সব অধিকার মুছলেন — মোড 000. ধাপ ৩-এ নতুন open() আবার যাচাই হলো, তাই Permission denied (EACCES). কিন্তু ধাপ ৪-এ পুরোনো fd দিয়ে read() পুরো ২২ byte পড়ে ফেলল — কারণ fd-টি নিজেই অনুমতির প্রমাণ, kernel আর inode-এর বিট দেখে না। ধাপ ৫-এ একই fd দিয়ে write() ব্যর্থ, Bad file descriptor (EBADF) — fd বহন করে শুধু read অধিকার, যা খোলার সময় চাওয়া হয়েছিল। এটিই delayed revocation: chmod নতুন open আটকায়, আগে দেওয়া capability কেড়ে নেয় না। (root হিসেবে চালালে ধাপ ৩-ও সফল হবে; আর NFS-এর মতো কিছু network file system-এ server প্রতিটি অনুরোধে আবার যাচাই করতে পারে।)
Capability-র delegation-ও fd-তে আছে: fork()-এ সন্তান fd পায়, আর Unix domain socket-এর SCM_RIGHTS বার্তায় একটি খোলা fd অন্য process-কে পাঠানো যায় — গ্রহীতা ফাইলটি নিজে খুলতে না পারলেও। Windows-এ DuplicateHandle() একই কাজ করে। উল্টো সতর্কতা: exec বা domain switch-এর আগে দরকারহীন fd বন্ধ না করলে তা নতুন প্রোগ্রামে চলে যায় — তাই O_CLOEXEC (পর্ব ১০)।
Capsicum, seL4 আর Fuchsia — capability-কেন্দ্রিক সিস্টেম
- Capsicum: Robert Watson ও সহকর্মীদের নকশা (USENIX Security, ২০১০), FreeBSD-তে যুক্ত।
cap_enter()ডাকার পর process আর global নাম (যেমন/etc/passwd) দিয়ে নতুন কিছু খুলতে পারে না, শুধু হাতে থাকা fd দিয়ে কাজ করে;cap_rights_limit()দিয়ে প্রতিটি fd-এর অধিকার আরও ছোট করা যায়। - seL4: একটি microkernel, যার সঠিকতা গাণিতিকভাবে প্রমাণিত (formal verification, ২০০৯)। Thread, address space, IPC endpoint, মেমরি — সব kernel object শুধু capability দিয়ে ছোঁয়া যায়।
- Fuchsia (Zircon kernel): Google-এর এই OS-এ process সব kernel object-কে অধিকারের বিটসহ handle দিয়ে ছোঁয়; কপি করার সময় অধিকার কমানো যায়, বাড়ানো নয়, আর প্রতিটি process শুধু তাকে দেওয়া namespace দেখে।
সতর্কতা — Linux-এর “capabilities” আলাদা জিনিস
Linux-এ (kernel ২.২ থেকে, ১৯৯৯) “capabilities” নামে একটি ব্যবস্থা আছে, কিন্তু তা Dennis–Van Horn-এর অর্থের capability নয়। পুরোনো Unix-এ ক্ষমতা ছিল দুই ভাগে — root (UID 0) সব পারে, বাকিরা কিছুই না। Linux root-এর ক্ষমতাকে প্রায় ৪০টি আলাদা অধিকারে ভেঙেছে: CAP_NET_BIND_SERVICE (১০২৪-এর নিচের port খোলা), CAP_CHOWN, CAP_DAC_OVERRIDE (ফাইল-permission এড়ানো), CAP_SYS_ADMIN (বহু admin কাজের বিশাল গুচ্ছ) ইত্যাদি। এগুলো process-এর বৈশিষ্ট্য — কোনো নির্দিষ্ট object-এর নাম বহন করে না, হস্তান্তরযোগ্য টোকেনও নয়; আসলে privilege. Least privilege-এর চমৎকার উপায়, কিন্তু capability-ভিত্তিক মডেল নয়।
| দিক | Capability (Dennis–Van Horn; fd, Capsicum, seL4) | Linux “capabilities” |
|---|---|---|
| কী বোঝায় | একটি নির্দিষ্ট object + তার ওপর অধিকার | একটি ধরনের privileged কাজ, যেকোনো object-এ |
| উদাহরণ | “fd 3: budget.txt, শুধু পড়া” | “CAP_CHOWN: যেকোনো ফাইলের মালিক বদলানো যায়” |
| হস্তান্তর | অন্য process-কে পাঠানো যায় (SCM_RIGHTS) | exec-এর নিয়মে উত্তরাধিকার, পাঠানোর টোকেন নয় |
| Matrix-এর ভাষায় | সারির একটি ঘর | বহু কলাম জুড়ে একটি বিশেষ সারি-বৈশিষ্ট্য |
প্রতিটি process-এর কয়েকটি capability-সেট থাকে: Permitted (যা সর্বোচ্চ চালু করতে পারে), Effective (এখন যা kernel সত্যিই যাচাইয়ে গোনে), Inheritable আর Ambient (exec-এর পরে কী থাকবে), আর Bounding (এই process আর তার বংশধর কখনো যা পেতে পারবে তার ঊর্ধ্বসীমা)। এগুলো /proc/self/status-এ ৬৪-বিট hex mask হিসেবে দেখা যায় — বিট n = capability নম্বর n. নিচের block সেগুলো পড়ে নিজেই decode করে, আর capsh থাকলে তার সাথে মিলিয়ে দেখে:
# Linux capabilities: root's power split into separate bits (bit n = one capability)
grep '^Cap' /proc/self/status
names=(chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap
linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner
sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice
sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap
mac_override mac_admin syslog wake_alarm block_suspend audit_read perfmon bpf
checkpoint_restore) # bit numbers 0..40, from linux/capability.h
decode() { # hex mask -> list of names
local v=$((16#$1)) out="" i
for ((i = 0; i < ${#names[@]}; i++)); do
if (( (v >> i) & 1 )); then out+="cap_${names[i]} "; fi
done
out=${out% }; echo "${out:-(none)}"
}
echo "--- decoded ---"
for f in CapEff CapPrm CapBnd; do
hex=$(awk -v k="$f:" '$1 == k {print $2}' /proc/self/status)
echo "$f = 0x$hex -> $(decode "$hex")"
done
if command -v capsh >/dev/null; then # cross-check with the libcap tool
echo "--- capsh --decode of CapBnd ---"
capsh --decode="$(awk '$1 == "CapBnd:" {print $2}' /proc/self/status)"
fiআউটপুট (এই runner-এর container-এ; আপনার মেশিনে সংখ্যা আলাদা হতে পারে):
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000800405fb
CapAmb: 0000000000000000
--- decoded ---
CapEff = 0x0000000000000000 -> (none)
CapPrm = 0x0000000000000000 -> (none)
CapBnd = 0x00000000800405fb -> cap_chown cap_dac_override cap_fowner cap_fsetid cap_kill cap_setgid cap_setuid cap_setpcap cap_net_bind_service cap_sys_chroot cap_setfcap
--- capsh --decode of CapBnd ---
0x00000000800405fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_sys_chroot,cap_setfcap
আমরা সাধারণ user wandbox, তাই CapEff আর CapPrm শূন্য — এই process-এর কোনো বিশেষ ক্ষমতা নেই। CapBnd = 0x800405fb ঊর্ধ্বসীমা: hex-কে বিটে ভাঙলে 0x5fb-এ বিট 0, 1, 3–8 আর 10; 0x40000-এ বিট 18; 0x80000000-এ বিট 31 — মোট ১১টি, যা decode()-এর লুপ আর capsh --decode দুটিই একভাবে দেখাল। সাধারণ Linux ডেস্কটপে bounding set-এ সাধারণত সব বিট চালু থাকে; container runtime তা অনেক ছোট করে — এখানে cap_sys_admin, cap_net_raw নেই। প্রতিরক্ষার দিক থেকে ব্যবহার: server-কে port 80 খুলতে দিতে পুরো root-এর বদলে setcap cap_net_bind_service=+ep ./server, systemd-এ AmbientCapabilities=, বা container-এ --cap-drop ALL --cap-add NET_BIND_SERVICE (পর্ব ৪৭ ও ৪৯)।
আরও গভীরে: Cambridge বিশ্ববিদ্যালয় আর SRI International-এর CHERI প্রকল্প পুরোনো tagged-capability ধারণাকে আধুনিক CPU-তে এনেছে: pointer-ই একটি ১২৮-বিটের capability, যাতে সীমা (bounds) আর অধিকার থাকে, আর মেমরির প্রতিটি capability-র সাথে একটি লুকানো tag বিট — সাধারণ লেখায় tag মুছে যায়, ফলে জাল pointer চলে না। Arm এর ভিত্তিতে Morello নামের পরীক্ষামূলক প্রসেসর-বোর্ড বানিয়ে গবেষকদের দিয়েছে (২০২২ সালের দিকে)। তত্ত্বের দিকে, Harrison, Ruzzo আর Ullman ১৯৭৬ সালে দেখান, সাধারণ access matrix মডেলে “একটি অধিকার কি কখনো ফাঁস হতে পারে” (safety problem) প্রশ্নটি অনির্ণেয় (undecidable) — আর take-grant-এর মতো সীমিত capability মডেলে প্রশ্নটি দক্ষভাবে সমাধানযোগ্য।
৮ · ACL বনাম capability — delegation, confused deputy আর revocation
| প্রশ্ন | ACL | Capability |
|---|---|---|
| “এই object কে ছুঁতে পারে?” | সহজ — object-এর তালিকা | কঠিন — সব domain ঘাঁটতে হয় |
| “এই domain কী পারে?” | কঠিন — সব object ঘাঁটতে হয় | সহজ — domain-এর তালিকা |
| প্রতিটি access-এর খরচ | প্রতিবার তালিকা খোঁজা (তাই বাস্তবে খোলার সময় একবার) | টোকেন দেখালেই হয় — দ্রুত |
| Least privilege | process ব্যবহারকারীর সব অধিকার নিয়ে চলে (ambient authority) | process শুধু হাতে দেওয়া টোকেন পায় — সূক্ষ্ম |
| Delegation (অন্যকে দেওয়া) | মালিককে ACL বদলাতে হয় | টোকেনটি পাঠালেই হয় (আর নিয়ন্ত্রণ করাও কঠিন) |
| Revocation (কেড়ে নেওয়া) | সহজ, তাৎক্ষণিক — entry মুছুন | কঠিন — টোকেন ছড়িয়ে আছে; বিশেষ কৌশল লাগে |
| Confused deputy | প্রবণ | স্বাভাবিকভাবে এড়ানো যায় |
Confused deputy — কার অধিকারে কাজটি হলো?
১৯৮৮ সালে Norm Hardy ACM Operating Systems Review-এ একটি ছোট কিন্তু প্রভাবশালী লেখা প্রকাশ করেন: “The Confused Deputy (or why capabilities might have been invented)”. ঘটনাটি Tymshare নামের একটি time-sharing কোম্পানির সিস্টেমে। একটি compiler-এর নিজস্ব ডিরেক্টরিতে লেখার অধিকার ছিল, কারণ সেখানে সে প্রতিটি ব্যবহারের বিল একটি ফাইলে ((SYSX)BILL) লিখত। ব্যবহারকারী compiler চালানোর সময় debug-আউটপুটের জন্য একটি ফাইলের নাম দিতে পারতেন। একজন ব্যবহারকারী আউটপুট-ফাইলের নাম দিলেন (SYSX)BILL — compiler নিজের অধিকারে ফাইলটি খুলে তাতে debug-আউটপুট লিখে বিলের তথ্য নষ্ট করে দিল। ব্যবহারকারীর নিজের সেই ফাইলে লেখার অধিকার ছিল না; compiler-এর ছিল — কিন্তু অন্য কাজের জন্য। Compiler “বিভ্রান্ত প্রতিনিধি”: সে দুই প্রভুর (নিজের আর ব্যবহারকারীর) কাজ করছিল, আর কোন কাজটি কার অধিকারে, তা আলাদা করতে পারেনি।
- ব্যবহারকারী
UBILL-এ লেখার অধিকার নেই - Compiler (deputy)
SYSXনিজের ডিরেক্টরিতে লিখতে পারে - বিলের ফাইল
(SYSX)BILLভুল জায়গায় লেখা - Capability
fdনাম + অধিকার একসাথে
U : write own files ............ yes U : write (SYSX)BILL ........... NO compiler : write (SYSX)BILL ........... yes (to record billing) compiler : write any file U names ..... ? (it runs with ITS rights)
U → compile("prog.f", out = "mydebug.txt")
compiler → open("mydebug.txt", write) kernel checks COMPILER's rights
kernel → granted
result : output lands in U's file — looks fine
U → compile("prog.f", out = "(SYSX)BILL")
compiler → open("(SYSX)BILL", write) kernel checks COMPILER's rights
kernel → granted (the compiler may write there!)
result : billing records overwritten by debug text
U → fd = open("mydebug.txt", write) checked with U's OWN rights
U → compile("prog.f", out = fd) passes the capability
compiler → write(fd, ...) no name lookup, no own rights used
U tries → open("(SYSX)BILL", write) → DENIED, so U has no fd to pass
compiler holds: fd_bill (its own capability, used ONLY for billing) compiler holds: fd_out (received from U, used ONLY for output) designation (which file) and authority (may I?) travel together → the deputy can no longer be confused about whose right it uses
- শুরুর অবস্থা: ব্যবহারকারী U বিলের ফাইলে লিখতে পারেন না; compiler পারে, কারণ বিল রাখা তার কাজ। Compiler চলে নিজের অধিকার নিয়ে — এটিই ambient authority.
- স্বাভাবিক কাজ: U আউটপুট-ফাইলের নাম দেন, compiler সেই নাম খোলে। Kernel যাচাই করে compiler-এর অধিকার, U-এর নয় — এখানে ক্ষতি নেই বলে সমস্যা চোখে পড়ে না।
- বিভ্রান্তি: U নাম দেন
(SYSX)BILL. Compiler নিজের অধিকারে খোলে, kernel মঞ্জুর করে, বিলের তথ্য নষ্ট হয়। ACL ঠিকই কাজ করেছে — প্রশ্ন ছিল “compiler কি লিখতে পারে?”, অথচ আসল প্রশ্ন হওয়া উচিত ছিল “U-এর এই অনুরোধে কি লেখা উচিত?” - Capability দিয়ে সমাধান: U নিজে ফাইলটি খোলেন — যাচাই হয় U-এর অধিকারে — আর compiler-কে নামের বদলে fd দেন। BILL খুলতে U-এর অধিকার নেই, তাই BILL-এর কোনো fd U-এর হাতে আসতেই পারে না।
- দুই টোকেন, দুই কাজ: compiler-এর হাতে নিজের বিল-fd আর U-এর দেওয়া আউটপুট-fd — দুটি আলাদা capability. “কোন ফাইল” (designation) আর “অধিকার আছে কি” (authority) একসাথে চলে, তাই deputy আর বিভ্রান্ত হতে পারে না।
একই সমস্যা আজ setuid প্রোগ্রাম, web-এর CSRF আর ক্লাউড-সেবায় দেখা যায় (ভাবনার প্রশ্ন ৩)। প্রতিরক্ষার সাধারণ নিয়ম: অনুরোধকারীর অধিকারে কাজ করুন (যেমন setuid প্রোগ্রাম ব্যবহারকারীর দেওয়া ফাইল খোলার আগে সাময়িকভাবে real UID-এ নেমে আসে), আর নামের বদলে খোলা handle নিন।
Revocation — দেওয়া অধিকার ফিরিয়ে নেওয়া
অধিকার কেড়ে নেওয়ার নকশা চারটি প্রশ্নে ভাগ হয়:
| প্রশ্ন | বিকল্প | উদাহরণ |
|---|---|---|
| কখন কার্যকর? | Immediate (তাৎক্ষণিক) বনাম delayed (পরে) | chmod নতুন open তখনই আটকায়, কিন্তু খোলা fd-কে নয় — delayed |
| কার কাছ থেকে? | Selective (কয়েকজনের) বনাম general (সবার) | শুধু রহিমের entry মুছুন বনাম “other” থেকে r সরান |
| কতটা? | Partial (কয়েকটি অধিকার) বনাম total (সব) | rw থেকে শুধু w সরানো বনাম পুরো entry মোছা |
| কতদিনের জন্য? | Temporary বনাম permanent | ছুটির সময় অ্যাকাউন্ট স্থগিত বনাম চাকরি ছাড়লে মুছে ফেলা |
ACL-এ চারটিই সহজ: object-এর তালিকায় গিয়ে entry মুছুন বা বদলান — পরের যাচাই থেকে কার্যকর। Capability-তে কঠিন, কারণ টোকেন domain-গুলোর হাতে ছড়িয়ে আছে। প্রচলিত কৌশল:
- Reacquisition: capability-গুলো সময়ে সময়ে মুছে ফেলা হয়; process আবার চায়, অধিকার বাতিল হলে পায় না — ক্লাউডের স্বল্পমেয়াদি access token-এর ধারণা।
- Back-pointer: object তার সব capability-র দিকে pointer রাখে, যাতে সবগুলো খুঁজে বাতিল করা যায়; সাধারণ, কিন্তু খরচ বেশি (বইগুলো MULTICS-এর উদাহরণ দেয়)।
- Indirection: capability নির্দেশ করে একটি global টেবিলের entry-কে, আর entry object-কে; entry মুছলে সব capability একসাথে অকেজো, তবে selective revocation হয় না।
- Key: object-এর একটি master key, capability তৈরির সময় তার কপি বসে; master key বদলালে পুরোনো সব capability অচল। কয়েকটি key রাখলে selective revocation-ও সম্ভব।
Unix-এর fd এর কোনোটিই পুরোপুরি দেয় না — chmod-এর পরেও খোলা fd চলে। ব্যবহারিক উপায়: process-টি পুনরায় চালু করা। BSD-র revoke() system call একটি device-এর সব খোলা fd অকেজো করে — বিরল একটি তাৎক্ষণিক revocation.
৯ · RBAC, DAC বনাম MAC, আর language-based protection
Role-based access control (RBAC)
বড় প্রতিষ্ঠানে প্রতিটি মানুষকে আলাদা আলাদা ফাইলে অধিকার দেওয়া অসম্ভব হয়ে ওঠে — কেউ বদলি হলে শত শত entry বদলাতে হয়। RBAC-এ অধিকার দেওয়া হয় role-কে (যেমন “হিসাবরক্ষক”, “শাখা-ব্যবস্থাপক”, “নিরীক্ষক”), আর মানুষকে দেওয়া হয় role. বদলি মানে শুধু role বদল। Access matrix-এর ভাষায় role হলো একটি domain, আর মানুষ সেই domain-এ ঢোকার অধিকার পান। ধারণাটিকে ১৯৯০-এর দশকে NIST-এর গবেষকেরা (David Ferraiolo, Richard Kuhn প্রমুখ) একটি মানসম্মত মডেলে রূপ দেন। Separation of duty সহজে লেখা যায় — “যে বিল তৈরি করে, সে একই বিল অনুমোদন করতে পারবে না।” উদাহরণ: Solaris-এর RBAC, Windows-এর group ও Active Directory-র role-ধরনের ব্যবহার, Kubernetes-এর RBAC, আর database-এর role (DBMS কোর্সের নিরাপত্তা পর্বে GRANT/REVOKE — ঠিক এই পর্বের access matrix-এর SQL রূপ)।
Discretionary বনাম mandatory
- DAC (discretionary access control): object-এর মালিক নিজের ইচ্ছায় (discretion) অধিকার দেন — Unix permission, POSIX ACL, Windows DACL. Matrix-এর owner আর copy অধিকার এই মডেলের মূল। দুর্বলতা: মালিকের নামে চলা যেকোনো প্রোগ্রাম — একটি ক্ষতিকর অ্যাপও — মালিকের মতোই অধিকার দিয়ে দিতে পারে।
- MAC (mandatory access control): একটি কেন্দ্রীয় নীতি, যা মালিকও বদলাতে পারেন না — যেমন “গোপন”-লেবেলের তথ্য “সাধারণ”-লেবেলের ফাইলে লেখা যাবে না (Bell–LaPadula মডেল, ১৯৭০-এর দশক)। আধুনিক রূপ SELinux, AppArmor, macOS ও iOS-এর sandbox প্রোফাইল, Windows-এর integrity level. DAC আর MAC সাধারণত একসাথে চলে: দুটিই “হ্যাঁ” বললে তবেই access — defense in depth. বিস্তারিত পর্ব ৪৯-এ।
Language-based protection — কম্পাইলার আর runtime-ও পাহারাদার
Kernel-এর protection-এর একক সাধারণত একটি process বা ফাইল। কিন্তু একই process-এর ভেতরে অনেক অংশ — একটি browser-এর ভেতরের বিভিন্ন সাইটের script, একটি অ্যাপের ভেতরের তৃতীয়-পক্ষের plugin. এখানে protection দিতে পারে programming language আর তার runtime: type safety (একটি পূর্ণসংখ্যাকে pointer বানানো যায় না), array-র সীমা যাচাই, আর মডিউলের দৃশ্যমানতা। একটি memory-safe ভাষায় object reference নিজেই এক ধরনের capability — যার হাতে reference নেই, সে object ছুঁতে পারে না।
Java-র উদাহরণ (সংক্ষেপে): JVM লোড করা bytecode আগে verifier দিয়ে যাচাই করে, যাতে জাল pointer বা ভুল type-এর কাজ না থাকে; class loader ঠিক করে কোড কোথা থেকে এসেছে। পুরোনো Java-তে SecurityManager প্রতিটি সংবেদনশীল কাজে কল-stack-এর প্রতিটি frame-এর অধিকার দেখত (stack inspection) — confused deputy ঠেকানোর একটি ভাষা-স্তরের চেষ্টা। মডেলটি জটিল আর কম-ব্যবহৃত প্রমাণিত হয়: Java 17-এ deprecated (JEP 411), JDK 24-এ স্থায়ীভাবে নিষ্ক্রিয় (JEP 486). আজ অবিশ্বস্ত কোড চলে আলাদা process বা container-এ, OS-এর sandbox দিয়ে — language-এর সুরক্ষা তার পরিপূরক, বিকল্প নয়।
১০ · AI-এর যুগে protection আর permission
“Permission denied” দেখে AI-কে জিজ্ঞেস করলে প্রায়ই সবচেয়ে দ্রুত — আর সবচেয়ে বিপজ্জনক — সমাধান আসে। বারবার দেখা ভুলগুলো:
| AI-এর ভুল | কেন ভুল | কীভাবে যাচাই করবেন |
|---|---|---|
chmod -R 777 বা “sudo দিয়ে চালান” | least privilege আর fail-safe default দুটিই ভাঙে; সব ব্যবহারকারী আর সব প্রোগ্রাম লিখতে পারে | কোন user আর কোন অধিকার আসলে দরকার তা id, ls -l, getfacl দিয়ে দেখে শুধু সেটুকু দিন |
| “chmod করলেই চলমান process আর পড়তে পারবে না” | খোলা fd/handle-এ যাচাই আগেই হয়ে গেছে — delayed revocation | এই পর্বের C প্রোগ্রাম; ls -l /proc/<pid>/fd |
| Linux capabilities আর object capability-কে এক বলা | প্রথমটি process-এর privilege-বিট, দ্বিতীয়টি object-নির্দিষ্ট টোকেন | man 7 capabilities; অংশ ৭-এর টেবিল |
Windows প্রশ্নে setfacl, বা Linux-এ icacls | দুই OS-এর ACL মডেল আর টুল আলাদা | প্রশ্নে OS আর version লিখুন; অফিসিয়াল ডকুমেন্টেশন |
| Access matrix-এ switch-কে দুই দিকের ধরে উত্তর | switch এক-দিকের; ফেরার জন্য আলাদা ঘর লাগে | পথ হাতে আঁকুন; এই পর্বের simulator-এ মেলান |
অনুমতির সমস্যায় AI-এর সাহায্য চাইলে প্রেক্ষাপট দিন, আর ন্যূনতম অধিকার চেয়ে নিন:
AI-এর উত্তর মানার আগে — চেকলিস্ট
পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:
- Protection আর security-র পার্থক্য? — protection হলো ভেতরের mechanism (কে কোন object-এ কী পারে); security বাইরের হুমকির বিরুদ্ধে পুরো সিস্টেমের সুরক্ষা — authentication, encryption ইত্যাদিসহ।
- Access matrix কী, আর এটি কীভাবে বাস্তবায়ন হয়? — সারি domain, কলাম object, ঘর অধিকার; global table, ACL (কলাম), capability list (সারি), lock–key.
- ACL আর capability-র মূল পার্থক্য? — ACL object-এর সাথে থাকে, revoke সহজ; capability domain-এর হাতে, access দ্রুত আর least privilege সহজ, কিন্তু revoke কঠিন।
- Copy, owner আর control অধিকারের পার্থক্য? — copy একই কলামে একটি অধিকার অন্যকে দেয়; owner কলামের যেকোনো ঘর বদলায়; control অন্য domain-এর পুরো সারি থেকে অধিকার মোছে।
- Confused deputy কী? — একটি বিশেষ-অধিকারের প্রোগ্রাম অন্যের অনুরোধে নিজের অধিকার ব্যবহার করে ফেলে; সমাধান: নামের বদলে capability পাঠানো (Hardy, ১৯৮৮)।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Capability ধারণাটি ১৯৬৬ সালের, আর গবেষকেরা বারবার একে উন্নততর বলেছেন। তবু Unix, Windows আর macOS ACL-এর ওপর দাঁড়াল কেন — আর capability কি এখন ফিরে আসছে?
Dennis ও Van Horn-এর ১৯৬৬ সালের গবেষণাপত্রের পর ১৯৭০-এর দশকে capability নিয়ে বহু উচ্চাভিলাষী প্রকল্প হয়: CMU-র Hydra, Cambridge-এর CAP কম্পিউটার, পরে IBM System/38 আর Intel-এর iAPX 432. প্রথম সমস্যা ছিল খরচ। Tagged hardware বা প্রতিটি pointer-এ বাড়তি যাচাই তখনকার মেশিনে ধীর আর দামি — iAPX 432 তার সময়ের প্রতিদ্বন্দ্বীদের তুলনায় খুব ধীর বলে পরিচিত, আর বাণিজ্যিকভাবে ব্যর্থ হয়; যদিও তার ধীরগতির সব দায় capability-র নয়, নকশার আরও অনেক সিদ্ধান্তের। দ্বিতীয় সমস্যা ছিল মানুষের চিন্তার ধরন: প্রশাসক আর ব্যবহারকারী ভাবেন “এই ফাইল কে কে দেখবে” — ACL ঠিক সেই প্রশ্নের উত্তর রাখে। তৃতীয়, revocation আর auditing ACL-এ সহজ, যা প্রতিষ্ঠানের নীতির জন্য জরুরি।
অন্যদিকে Unix-এর সরলতা — ৯টি বিট — এত সফল হয় যে বিকল্প নিয়ে ভাবার চাপ কমে যায়। তবু লক্ষ করুন, ACL-ভিত্তিক সিস্টেমগুলোও ভেতরে capability ব্যবহার করে: fd আর Windows handle হলো খোলার সময়ে ACL যাচাই করে বানানো capability. অর্থাৎ বাস্তবের উত্তর ছিল দুটির মিশ্রণ।
এখন capability আবার শক্তিশালী হয়ে ফিরছে, মূলত নিরাপত্তার কারণে। Browser আর মোবাইল অ্যাপের sandbox-এ “ব্যবহারকারীর সব অধিকার নিয়ে চলা” (ambient authority) বিপজ্জনক প্রমাণিত হয়েছে। FreeBSD-র Capsicum, seL4, Google-এর Fuchsia, WebAssembly-র WASI (যেখানে প্রোগ্রাম শুধু হাতে দেওয়া ডিরেক্টরি-handle পায়), আর হার্ডওয়্যারে CHERI — সবাই একই কথা বলে: প্রোগ্রাম যা ছুঁতে পারবে তা নামে নয়, হাতে দেওয়া টোকেনে ঠিক হোক। Android-এর intent-এ URI permission দেওয়াও একই ধারার — একটি নির্দিষ্ট ফাইলে সাময়িক অধিকার, পুরো storage নয়।
মূল উপলব্ধি: ACL জিতেছিল দাম, প্রশাসনের সুবিধা আর revocation-এর জোরে; capability ফিরছে কারণ আজকের প্রধান হুমকি নিজের নামে চলা অবিশ্বস্ত কোড, যাকে শুধু প্রয়োজনীয় টোকেন দেওয়াই least privilege-এর সবচেয়ে সরাসরি রূপ।
প্র ০২Saltzer ও Schroeder-এর নীতিগুলো প্রায় ৫০ বছরের পুরোনো। বাস্তবের বড় OS-গুলো কোথায় সেগুলো ভেঙেছিল, আর কীভাবে ফিরে এল?
সবচেয়ে চেনা উদাহরণ fail-safe defaults আর least privilege. Windows XP যুগে বাড়ির কম্পিউটারে সাধারণত প্রথম অ্যাকাউন্টটিই administrator হতো, আর বহু প্রোগ্রাম ধরে নিত যে সে admin অধিকারে চলবে। ফলে ব্যবহারকারীর ভুলে খোলা একটি ক্ষতিকর ফাইল পুরো সিস্টেমের মালিক হয়ে যেত। ২০০৭ সালের Windows Vista-তে User Account Control (UAC) আসে: admin-ও সাধারণত সাধারণ অধিকারে চলেন, আর বিশেষ কাজে আলাদা নিশ্চিতকরণ দিতে হয়। এখানে আবার psychological acceptability-র শিক্ষা মিলল — প্রথম দিকে এত বেশি পপ-আপ আসত যে মানুষ না পড়েই “হ্যাঁ” চাপতেন; পরের সংস্করণে সংখ্যা কমিয়ে ভারসাম্য খোঁজা হয়।
Complete mediation-এর সাথে সবসময় একটি টানাপোড়েন থাকে: প্রতিটি read-এ ACL খুঁজলে সিস্টেম ধীর হয়, তাই Unix আর Windows যাচাইয়ের ফল fd বা handle-এ জমা রাখে। এই পর্বের C প্রোগ্রামে দেখলেন এর দাম — chmod-এর পরেও পুরোনো fd কাজ করে। আরেকটি দাম “time of check to time of use” (TOCTOU): একটি প্রোগ্রাম প্রথমে পরীক্ষা করে ফাইলটি নিরাপদ কি না, তারপর খোলে — মাঝের ফাঁকে অন্য কেউ ফাইলটিকে symbolic link দিয়ে বদলে দিতে পারে। প্রতিরক্ষা: আলাদা পরীক্ষা না করে সরাসরি খোলা আর খোলা handle-এর ওপর যাচাই (openat, O_NOFOLLOW, fstat).
Economy of mechanism বড় OS-এ সবচেয়ে কঠিন: Linux kernel কয়েক কোটি লাইনের, আর পুরোটাই TCB. Microkernel আর seL4-এর মতো প্রকল্প এর উত্তর, আর মূলধারায় এর ছায়া দেখা যায় privilege-ভাগে — browser-এর আলাদা renderer process, OpenSSH-এর privilege separation, যেখানে নেটওয়ার্কের কাঁচা ডেটা সামলায় একটি অল্প-অধিকারের অংশ। Open design আজ প্রায় সর্বসম্মত: প্রকাশ্য encryption মানদণ্ড, খোলা কোড আর bug bounty.
মূল উপলব্ধি: নীতিগুলো পুরোনো হয়নি; বাস্তবের সিস্টেম সুবিধা আর গতির জন্য সেগুলো থেকে সরে গিয়েছিল, আর বড় নিরাপত্তা-ব্যর্থতার পর বারবার সেখানেই ফিরে এসেছে।
প্র ০৩Confused deputy-র গল্পটি একটি ১৯৮০-এর দশকের compiler-এর। আজকের web, ক্লাউড আর মোবাইলে এটি কোন রূপে দেখা যায়, আর প্রতিরক্ষা কী?
Hardy-র লেখার মূল পর্যবেক্ষণ ছিল: সমস্যাটি compiler-এর বাগ নয়, ব্যবস্থার নকশার। যখন একটি প্রোগ্রাম একটি নাম পায় আর সেই নাম নিজের অধিকারে খোলে, তখন নাম কোথা থেকে এসেছে তা হারিয়ে যায়। “কোন জিনিস” আর “কার অধিকারে” আলাদা পথে চলে — আর বিভ্রান্তি সেখানেই। এই কাঠামো আজ বহু জায়গায় ফিরে আসে।
Web-এ CSRF (cross-site request forgery): browser প্রতিটি অনুরোধের সাথে একটি সাইটের cookie নিজে থেকেই পাঠায় — ambient authority. অন্য একটি সাইট আপনার browser দিয়ে আপনার ব্যাংকের ঠিকানায় একটি অনুরোধ পাঠালে browser cookie জুড়ে দেয়, আর ব্যাংক ভাবে আপনি চেয়েছেন। প্রতিরক্ষা: প্রতিটি form-এর সাথে একটি অনুমান-করা-যায়-না এমন টোকেন (যা আসলে একটি ছোট capability), আর cookie-র SameSite বৈশিষ্ট্য, যা অন্য সাইট থেকে আসা অনুরোধে cookie পাঠানো সীমিত করে।
ক্লাউডে: একটি সেবা যদি ব্যবহারকারীর দেওয়া storage-এর নাম নিজের বিস্তৃত অধিকারে খোলে, তাহলে একজন গ্রাহক অন্য গ্রাহকের ডেটার নাম দিয়ে তা পড়িয়ে নিতে পারেন। তাই বড় ক্লাউড প্রদানকারীরা cross-account role-এ “external ID” বা অনুরোধের উৎস-পরিচয়ের শর্ত রাখার পরামর্শ দেন, আর ডকুমেন্টেশনে একে সরাসরি “confused deputy problem” নামেই ডাকেন। মোবাইলে: একটি অ্যাপ অন্য অ্যাপের অনুরোধে নিজের permission দিয়ে কোনো কাজ করে দিলে (permission re-delegation) একই সমস্যা; Android-এ নির্দিষ্ট URI-তে সাময়িক অধিকার দেওয়ার ব্যবস্থা এর capability-ধরনের প্রতিকার।
সাধারণ প্রতিরক্ষার তিনটি নিয়ম: অনুরোধকারীর অধিকারে যাচাই করুন, নিজের নয়; সম্ভব হলে নামের বদলে খোলা handle বা স্বাক্ষরিত টোকেন নিন; আর প্রতিটি privileged কাজে লিখে রাখুন কার পক্ষে কাজটি হচ্ছে।
মূল উপলব্ধি: যেখানেই একটি প্রোগ্রাম অন্যের দেওয়া নাম নিজের অধিকারে ব্যবহার করে, সেখানে confused deputy-র ঝুঁকি; নাম আর অধিকার একসাথে বেঁধে দেওয়াই স্থায়ী সমাধান।
প্র ০৪“এই সিস্টেমে কি কখনো কোনো অবাঞ্ছিত domain একটি অধিকার পেয়ে যেতে পারে?” — এই প্রশ্নের উত্তর কেন সাধারণভাবে বের করা যায় না, আর বাস্তবে আমরা কী করি?
Access matrix শুধু একটি স্থির টেবিল নয় — copy, owner আর control অধিকারের মাধ্যমে টেবিল নিজেকে বদলায়, আর নতুন domain ও object তৈরি হয়। তাই প্রশ্নটি হলো: শুরুর একটি matrix আর বদলের নিয়মগুলো দেওয়া থাকলে, কোনো ধারাবাহিক বদলের পর কি অধিকার r এমন কোনো ঘরে পৌঁছাতে পারে, যেখানে আগে ছিল না? একে বলে safety problem.
১৯৭৬ সালে Michael Harrison, Walter Ruzzo আর Jeffrey Ullman (HRU মডেল) দেখান, বদলের নিয়ম যথেষ্ট সাধারণ হলে এই প্রশ্নটি অনির্ণেয় — কোনো algorithm সব ক্ষেত্রে সঠিক উত্তর দিতে পারে না। প্রমাণের মূল ভাবনা: matrix আর তার বদলের নিয়ম দিয়ে একটি Turing machine-কে অনুকরণ করা যায় — প্রতিটি নতুন domain টেপের একটি ঘর, অধিকারগুলো ঘরের চিহ্ন আর মাথার অবস্থান। তখন “অধিকারটি ফাঁস হবে কি না” প্রশ্নটি “machine-টি থামবে কি না” (halting problem)-এর সমান কঠিন হয়ে যায়, যা অনির্ণেয় বলে প্রমাণিত। তবে তাঁরা এটিও দেখান যে প্রতিটি বদল-নিয়মে মাত্র একটি মৌলিক কাজ থাকলে (mono-operational) প্রশ্নটি নির্ণয়যোগ্য, যদিও কঠিন।
এর পরেই সীমিত মডেলের খোঁজ শুরু হয়। Take-grant মডেলে (Jones, Lipton, Snyder প্রমুখের ১৯৭০-এর দশকের কাজ) অধিকার হস্তান্তরের নিয়ম এতটাই সীমিত যে safety প্রশ্ন graph-এর আকারের তুলনায় দ্রুত সমাধান হয়। বাস্তবের শিক্ষা তিনটি। প্রথমত, অধিকার-হস্তান্তরের নিয়ম যত সরল, বিশ্লেষণ তত সম্ভব — economy of mechanism-এর গাণিতিক যুক্তি। দ্বিতীয়ত, MAC নীতি (পর্ব ৪৯) অনেকটা এই কারণেই জনপ্রিয়: মালিকের ইচ্ছায় অধিকার ছড়ানো বন্ধ রাখলে “কে কী পেতে পারে” প্রশ্নের উত্তর নীতি দেখেই বলা যায়। তৃতীয়ত, বড় সিস্টেমে নিয়মিত audit — বর্তমান অধিকার কী, তা মাপা — তত্ত্বে যা প্রমাণ করা যায় না, তার বাস্তব বিকল্প।
মূল উপলব্ধি: নমনীয় অধিকার-হস্তান্তরের দাম বিশ্লেষণের অক্ষমতা; নিরাপদ নকশা তাই ইচ্ছাকৃতভাবে নিয়ম সীমিত রাখে, যাতে “কী হতে পারে” প্রশ্নের উত্তর দেওয়া যায়।
প্র ০৫Linux-এ root-এর ক্ষমতা প্রায় ৪০টি টুকরোয় ভাঙা হয়েছে। তবু কেন বলা হয় CAP_SYS_ADMIN “নতুন root”? আর container চালানোর সময় এই জ্ঞান কীভাবে প্রতিরক্ষায় কাজে লাগে?
ভাগ করার উদ্দেশ্য ছিল least privilege: যে প্রোগ্রামের শুধু ঘড়ি ঠিক করার দরকার, সে পাবে শুধু CAP_SYS_TIME; যে ping পাঠায়, সে পাবে CAP_NET_RAW. সমস্যা দেখা দিল নতুন বৈশিষ্ট্য যোগের সময়। Kernel-এ নতুন কোনো privileged কাজ এলে ডেভেলপারদের ঠিক করতে হয় কোন capability দিয়ে তা রক্ষা হবে, আর বহু বছর ধরে সহজ উত্তর ছিল “CAP_SYS_ADMIN”. ফলে এই একটি বিট mount করা, namespace-এর অনেক কাজ, বহু device-নিয়ন্ত্রণ আর আরও অসংখ্য কাজ বহন করে — Linux-এর নিজস্ব man page (capabilities(7)) নতুন কাজে এটি ব্যবহার না করতে সতর্ক করে আর একে “overloaded” বলে। অনেক ক্ষেত্রে CAP_SYS_ADMIN থাকলে বাকি অধিকার পাওয়ার পথও খুলে যায়, তাই কার্যত এটি প্রায় root. এরপর নতুন সংকীর্ণ capability যোগ হয়েছে — যেমন performance মাপার জন্য CAP_PERFMON, eBPF-এর জন্য CAP_BPF (Linux ৫.৮-এর সময়) — যাতে এই বোঝা কমে।
Container-এ এই জ্ঞান সরাসরি প্রতিরক্ষায় লাগে। Container-এর “root” সাধারণত একই host kernel-এর ওপর চলে; container runtime তাই ডিফল্টভাবে capability-র bounding set ছোট করে দেয় — এই পর্বের runner-এ দেখলেন মাত্র ১১টি বিট। ভালো চর্চা: --cap-drop ALL দিয়ে সব বাদ দিয়ে শুধু দরকারিগুলো যোগ করা; --privileged এড়িয়ে চলা, কারণ এটি প্রায় সব capability আর device-অধিকার ফিরিয়ে দেয়; container-এর ভেতরে non-root user ব্যবহার; আর seccomp ও MAC প্রোফাইল চালু রাখা (পর্ব ৪৯, ৫১)। যাচাইয়ের সহজ উপায় এই পর্বের bash block-টিই: container-এর ভেতরে /proc/self/status-এর CapEff আর CapBnd দেখে নিন, আর প্রত্যাশার সাথে মেলান।
এখান থেকে নকশার একটি সাধারণ শিক্ষাও আসে: privilege ভাগ করা তখনই কাজের, যখন প্রতিটি টুকরো সত্যিই সংকীর্ণ থাকে। একটি টুকরো বড় হতে থাকলে পুরো ব্যবস্থা আবার “সব বা কিছুই না”-তে ফিরে যায়।
মূল উপলব্ধি: least privilege শুধু mechanism-এ আসে না, শৃঙ্খলায় আসে — প্রতিটি নতুন ক্ষমতাকে সংকীর্ণ রাখতে হয়, আর প্রতিটি deployment-এ বাস্তবে কোন বিট চালু, তা মেপে দেখতে হয়।
অনুশীলন
হাতে trace, আউটপুট অনুমান, কমান্ড আর নকশা — আগে নিজে চেষ্টা করুন, তারপর উত্তর মেলান।
-
হাতে trace: একটি access matrix — A: {X: read; B: switch}, B: {Y: read, write; C: switch}, C: {Q: print; X: write}. (ক) A-তে চলা process কি X-এ লিখতে পারে? (খ) C কি Y পড়তে পারে? (গ) B কি X পড়তে পারে? (ঘ) ACL(X) আর C-list(B) লিখুন। (ঙ) A থেকে কোন কোন domain-এ পৌঁছানো যায়?
(ক) হ্যাঁ — A → B → C পথে; (C, X)-এ write আছে। (খ) না — (C, Y) খালি, আর C-এর সারিতে কোনো switch নেই। (গ) না — B থেকে পৌঁছানো যায় B আর C; (B, X) খালি, (C, X)-এ শুধু write; read আছে শুধু A-তে, আর B থেকে A-তে পথ নেই। (ঘ) ACL(X) = (A, read), (C, write); C-list(B) = (Y, read write), (C, switch). (ঙ) A, B, C — তিনটিই।
যাচাই: অংশ ৪-এর simulator-এ
Mবদলে{ A: { X: "r", B: "s" }, B: { Y: "rw", C: "s" }, C: { Q: "p", X: "w" } }বসিয়ে["A", "w", "X"]ইত্যাদি প্রশ্ন চালান — উত্তর আসেGRANTED via A -> B -> C, আর বাকি দুটিতেDENIED. -
আউটপুট অনুমান: অংশ ৭-এর C প্রোগ্রামে শুধু
chmod(path, 0);লাইনটি তুলে প্রথমopen()-এর আগে বসানো হলো (fclose(f);-এর ঠিক পরে)। সাধারণ (non-root) user হিসেবে চালালে stdout-এ, stderr-এ কী আসবে, exit status কত, আর ফোল্ডারে কী থেকে যাবে?stdout-এ কিছুই না। প্রথম
open()-এর সময়েই যাচাই হয়, মোড000, তাই ব্যর্থ —perror("open")stderr-এ লেখেopen: Permission denied, আরreturn 1— exit status 1.unlink()পর্যন্ত পৌঁছায় না, তাইbudget.txtমোড----------নিয়ে থেকে যায় (মালিক হিসেবে আপনিchmodদিয়ে মোড ফেরাতে পারবেন, আরrmদিয়ে মুছতেও পারবেন — মোছার অধিকার আসে ডিরেক্টরির w-বিট থেকে, ফাইলের মোড থেকে নয়)। শিক্ষা: capability পাওয়ার মুহূর্তেই যাচাই, তাই যাচাইয়ের আগে অধিকার কেড়ে নিলে টোকেনই আসে না। -
কমান্ড লিখুন: একটি Linux সার্ভারে
report.txt-এর মালিকkarim, groupstaff, মোড640. ব্যবহারকারীrahim(staff-এর সদস্য নন)-কে শুধু পড়ার অধিকার দিন, group বা মোড না বদলে; যাচাই করুন; তারপর অধিকারটি ফিরিয়ে নিন। Windows-এ একই কাজের কমান্ড কী?setfacl -m u:rahim:r report.txt # একটি নামযুক্ত user entry যোগ getfacl report.txt # user:rahim:r-- আর mask::r-- দেখা যাবে ls -l report.txt # -rw-r-----+ ('+' মানে extended ACL) sudo -u rahim cat report.txt # (admin হিসেবে) রহিম হিসেবে পরীক্ষা setfacl -x u:rahim report.txt # selective, total revocationমনে রাখুন: রহিমকে ফাইলে পৌঁছাতে পথের প্রতিটি ডিরেক্টরিতে x অধিকারও লাগবে। আর রহিম ইতিমধ্যে কোনো process-এ ফাইলটি খুলে রাখলে
setfacl -x-এর পরেও সেই fd দিয়ে পড়তে পারবেন — delayed revocation. Windows-এ:icacls report.txt /grant "DOMAIN\rahim:(R)", যাচাইicacls report.txt, ফিরিয়ে নিতেicacls report.txt /remove "DOMAIN\rahim". -
নকশা: একটি বোর্ডের ফলাফল-প্রকাশ সিস্টেমে ১০০ জন পরীক্ষক নম্বর লেখেন। ফল প্রকাশের দিন সকাল ১০টায় সবার লেখার অধিকার একসাথে আর তাৎক্ষণিকভাবে বন্ধ করতে হবে, আর একজন পরীক্ষককে কোনো সময় আলাদাভাবেও বাদ দিতে হতে পারে। ACL আর capability-র দৃষ্টিতে কীভাবে নকশা করবেন? Revocation-এর চার প্রশ্নে আপনার নকশা কোথায় পড়ে?
ACL/RBAC পথ: একটি role “examiner”, নম্বর-টেবিলের ACL-এ শুধু এই role-এর write. ১০টায় role-এর write মুছলে সবার জন্য একসাথে বন্ধ — general, partial (read থাকতে পারে), permanent; একজনকে role থেকে সরানো — selective. তবে “তাৎক্ষণিক” হতে খোলা session আর connection-ও বন্ধ করতে হবে, কারণ সেগুলো আগের যাচাই বহন করে (delayed revocation-এর ফাঁদ)।
Capability পথ: প্রতিটি পরীক্ষকের টোকেন একটি মধ্যবর্তী entry-কে নির্দেশ করে (indirection); ১০টায় entry মুছলে সব টোকেন একসাথে অকেজো — immediate, general. আলাদা বাদের জন্য পরীক্ষকভেদে আলাদা entry বা key, অথবা স্বল্পমেয়াদি টোকেন (reacquisition). বাস্তবে মিশ্রণই ভালো: role দিয়ে নীতি, স্বল্পমেয়াদি টোকেন দিয়ে session, আর প্রতিটি লেখার audit log.
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমUnix-ধরনের সিস্টেমে একটি process ফাইল খুলে পড়তে চাইলে protection-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- Process
open()system call করে, kernel mode-এ ঢোকে - Kernel পথের প্রতিটি ডিরেক্টরিতে search (x) অধিকার যাচাই করে
- Process-এর effective UID/GID ফাইলের owner/group/other বিট বা ACL-এর সাথে মেলানো হয়
- অনুমতি মিললে চাওয়া মোডসহ একটি open-file entry তৈরি হয় আর process একটি fd পায়
- পরের
read()-এ kernel শুধু fd-এর মোড দেখে, permission-বিট আর দেখে না
Access matrix-এর সারি domain আর কলাম object হলে, একটি access control list (ACL) matrix-এর কোন অংশ?
- একটি সারি, খালি ঘর বাদ দিয়ে
- একটি কলাম, খালি ঘর বাদ দিয়ে
- প্রধান কর্ণ (diagonal)
- সব ভরা ঘরের <domain, object, rights> তালিকা
একটি access matrix-এ D1-এর সারিতে আছে (F1: read) আর (D2: switch); D2-এর সারিতে আছে (F2: write) আর (D3: switch); D3-এর সারিতে (F1: write). D1-এ শুরু করা একটি process কী কী করতে পারে?
- শুধু F1 পড়তে
- F1 পড়তে আর F2-এ লিখতে, কিন্তু F1-এ লিখতে নয়
- F1 পড়তে, F2-এ লিখতে আর F1-এ লিখতে — প্রতিটি উপযুক্ত domain-এ গিয়ে
- কিছুই না, কারণ switch করলে আগের অধিকার হারায়
Linux-এ একটি process একটি ফাইল O_RDONLY-এ খুলে fd পেল। তারপর মালিক chmod 000 করলেন। Process (non-root) সেই পুরোনো fd দিয়ে read() করলে কী হবে?
- পড়া সফল হবে, কারণ যাচাই হয়েছিল open-এর সময়ে
- EACCES (Permission denied) দিয়ে ব্যর্থ হবে
- EBADF (Bad file descriptor) দিয়ে ব্যর্থ হবে
- Kernel process-টিকে SIGKILL পাঠাবে
Linux-এর CAP_NET_BIND_SERVICE সম্পর্কে কোন কথাটি সঠিক?
- এটি একটি নির্দিষ্ট socket-এর জাল-করা-যায়-না এমন object capability
- এটি থাকলে process firewall আর routing টেবিল বদলাতে পারে
- এটি দিলে process কার্যত root হয়ে যায়
- এটি দিয়ে root ছাড়াই ১০২৪-এর নিচের port-এ bind করা যায়
একটি privileged সেবা ব্যবহারকারীর দেওয়া ফাইলের নাম নিজের অধিকারে খুলে তাতে লেখে, আর একজন ব্যবহারকারী এমন একটি ফাইলের নাম দিলেন যাতে তাঁর নিজের লেখার অধিকার নেই। এই সমস্যার নাম আর সবচেয়ে মৌলিক সমাধান কী?
- Deadlock; lock-এর ক্রম ঠিক করা
- Race condition; mutex ব্যবহার
- Confused deputy; নামের বদলে ব্যবহারকারীর খোলা capability (fd/handle) পাঠানো
- Privilege escalation; সেবাটিকে root হিসেবে চালানো
Access matrix-এর একটি ঘরে read* লেখা থাকলে “*” কী বোঝায়?
- ওই domain-এর সারির সব অধিকার বদলানো যায়
- ওই domain ‘read’ অধিকারটি একই object-এর জন্য অন্য domain-কে দিতে পারে
- ওই domain object-এর মালিক, তাই কলামের সব ঘর বদলাতে পারে
- ওই domain অন্য domain-এ switch করতে পারে
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Protection বনাম security: protection ভেতরের mechanism — কে কোন object-এ কী পারে; security বাইরের হুমকিসহ পুরো সিস্টেমের সুরক্ষা। নীতি: least privilege, fail-safe default, complete mediation, economy of mechanism, open design (Saltzer–Schroeder, ১৯৭৫), সাথে need-to-know, compartmentalisation, defense in depth.
- Domain আর matrix: domain = <object, rights> জোড়ার সেট; matrix-এর সারি domain, কলাম object. বিশেষ অধিকার: copy (*, একই কলামে), owner (কলাম বদলায়), control (সারি বদলায়), switch (domain-কলামে, এক-দিকের পথ)। “Switch করে পারবে কি” = graph-এ reachability.
- বাস্তবায়ন: global table; ACL = কলাম (Unix-এর ৯টি বিট, POSIX ACL-এর
+আর mask, Windows DACL/ACE — deny আগে, মেলেনি মানে না); capability list = সারি (জাল-অযোগ্য: tagged hardware, kernel টেবিল, cryptography); lock–key. - fd একটি capability: যাচাই হয় open-এর সময়ে, তাই chmod-এর পরেও পুরোনো fd পড়ে, কিন্তু মোডের বাইরে (write) পারে না। Linux “capabilities” (CapEff/CapPrm/CapBnd, CAP_NET_BIND_SERVICE) root-এর ক্ষমতার টুকরো — object capability নয়।
- তুলনা: ACL-এ revoke সহজ, confused deputy-প্রবণ; capability-তে least privilege আর delegation সহজ, revoke কঠিন (reacquisition, back-pointer, indirection, key). Revocation: immediate/delayed, selective/general, partial/total, temporary/permanent. RBAC role দিয়ে অধিকার দেয়; DAC মালিকের ইচ্ছায়, MAC কেন্দ্রীয় নীতিতে (পর্ব ৪৯)।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪৭ · Linux-এ user, group আর permission — rwx, setuid আর sudoপরবর্তী পর্বএই পর্বের ACL আর domain switch Linux-এ বাস্তবে কীভাবে চলে।
- অপারেটিং সিস্টেম · কোর্স হোম৬৪টি পর্বপুরো কোর্সের রোডম্যাপ — কোন পর্ব কখন।
- পর্ব ৪১ · ফাইল — file descriptor, open/read/write আর access methodপেছনে ফিরেfd, open-file table আর file attribute — capability-র ভিত্তি।
- পর্ব ০৬ · OS-এর গঠন — monolithic, microkernel, hybrid আর moduleপেছনে ফিরেTrusted computing base, policy বনাম mechanism আর seL4-এর মতো microkernel.
- DBMS · ডেটাবেস নিরাপত্তা — privilege আর GRANT/REVOKEডেটাবেসDatabase-এ access matrix আর role-এর বাস্তব রূপ।
- DSA · Graphঅ্যালগরিদমBFS আর reachability — switch-পথ খোঁজার পেছনের algorithm.