سؤال

أنا أكتب بعض البرامج (في C ++ ، لـ Linux/Mac OSX) التي تعمل كمستخدم غير محدد ولكنه يحتاج إلى امتيازات الجذر في مرحلة ما (لإنشاء جهاز افتراضي جديد).

إن تشغيل هذا البرنامج كجذر ليس خيارًا (بشكل أساسي لمشكلات الأمان) وأحتاج إلى معرفة هوية المستخدم "الحقيقي".

هل هناك طريقة لتقليد سلوك أمر "sudo" (اطلب كلمة مرور المستخدم) للحصول على امتيازات الجذر مؤقتًا وأداء المهمة المحددة؟ إذا كان الأمر كذلك ، فما هي الوظائف التي سأستخدمها؟

شكرا جزيلا لك على مساعدتك !

هل كانت مفيدة؟

المحلول

الإجابة الأصلية

قد تفكر في مفتاح setuid على القابل للتنفيذ نفسه. ويكيبيديا لديها مقالة - سلعة عليه حتى يظهر لك الفرق بين geteuid() و getuid() بشكل فعال تمامًا ، فإن الأول هو معرفة من "المحاكاة" والأخير لك ". عملية sudo ، على سبيل المثال ، يجب أن تُرجع geteuid 0 (الجذر) وإحضار معرف المستخدم الخاص بك ، ومع ذلك ، فإن عملياتها الفرعية تعمل حقًا على أنها جذر (يمكنك التحقق من ذلك sudo id -u -r).

لا أعتقد أن هناك طريقة للوصول إلى الجذر بسهولة برمجيًا - بعد كل شيء ، بتطبيق مبدأ الامتياز الأقل ، لماذا تحتاج إلى ذلك؟ الممارسة الشائعة هي تشغيل أجزاء محدودة فقط من الكود مع امتيازات مرتفعة. يتم إنشاء الكثير من الشيطات وغيرها أيضًا تحت أنظمة حديثة لتشغيلها كمستخدم خاص بها مع معظم الامتيازات التي يحتاجونها. فقط للعمليات المحددة للغاية (التثبيت وما إلى ذلك) ، هناك حاجة حقًا إلى امتيازات الجذر.

تحديث 2013

إن إجابتي الأصلية تقف (على الرغم من أن نفسي 2013 قد تقوم بعمل أفضل من ذلك من عام 2010) ، ولكن إذا كنت تقوم بتصميم تطبيق يتطلب الوصول إلى الجذر ، فقد ترغب في التفكير بالضبط في نوع الوصول إلى الجذر والنظر في استخدام قدرات Posix (صفحة الرجل). هذه مختلفة عن الأمن القائم على القدرة كما تم تنفيذها في L4 وآخرون. تسمح إمكانيات Posix بمنح طلبك مجموعة فرعية من صلاحيات Root. علي سبيل المثال CAP_SYS_MODULE سيسمح لك بإدخال وحدات kernel ، ولكن لا تعطيك أي قوى جذر أخرى. هذا قيد الاستخدام في التوزيعات على سبيل المثال لدى Fedora ميزة لإزالة ثنائيات setuid بالكامل مع الوصول إلى الجذر العشوائي.

هذا مهم لأنه كمبرمج ، من الواضح أن الكود الخاص بك مثالي! لكن المكتبات التي تعتمد عليها (تنهد ، إذا كتبتها فقط!) قد يكون لها نقاط ضعف فيها. باستخدام القدرات ، يمكنك الحد من استخدام هذا الاستغلال ، وإنقاذ نفسك وشركتك من التدقيق المتعلق بالأمان. هذا يجعل الجميع أكثر سعادة.

نصائح أخرى

إذا كنت بحاجة إلى امتيازات الجذر في كل مرة ، فإن أفضل شيء هو بدء برنامجك كجذر وإسقاطها (في عملية فرعية) مع setuid و setgid. هذا ما يفعله أباتشي عندما يحتاج إلى ربط المنفذ المقيد 80.

إذا كان الحصول على حقوق الجذر هو الاستثناء بدلاً من القاعدة ويتم تشغيل البرنامج بشكل تفاعلي ، فإن هناك طريقة أخرى تتمثل في كتابة برنامج add_interface والتنفيذ

sudo add_interface args

ودع sudo التعامل مع المصادقة لك. بدلاً من Sudo ، قد ترغب في استخدام واجهة رسومية مثل GKSU أو GKSUDO أو KDESU أو KDESUDO. لن أحاول تنفيذ إدخال كلمة مرور آمنة بنفسي ؛ يمكن أن تكون مشكلة صعبة ، وربما تترك ثقوبًا أمانًا ومشاكل وظيفية (هل تدعم قراء بصمات الأصابع؟).

بديل آخر هو بولكيت, ، ودعا سابقا policykit.

لا يمكنك الحصول على امتيازات الجذر ، يجب أن تبدأ معهم وتقليل امتيازاتك حسب الحاجة. الطريقة المعتادة التي تقوم بها هي تثبيت البرنامج مع مجموعة بت "setuid": هذا يعمل على تشغيل البرنامج مع معرف المستخدم الفعال لمالك الملف. اذا ركضت ls -l تشغيل sudo, ، سترى أنه مثبت بهذه الطريقة:

-rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo

أثناء تشغيل برنامجك بامتيازات الجذر ، يمكنك الاتصال بـ setuid(2) استدعاء النظام لتغيير معرف المستخدم الفعال إلى بعض المستخدمين غير المحمصين. أعتقد (لكن لم تجرب هذا) أنه يمكنك تثبيت برنامجك كجذر مع setuid bit ، وتقليل الامتياز على الفور ، ثم استعادة الامتياز حسب الحاجة (من الممكن ، ومع ذلك ، أنه بمجرد خفض امتيازك لن تفعل تكون قادرة على استعادته).

يتمثل الحل الأفضل في تحريك جزء البرنامج الذي يحتاج إلى تشغيل الجذر ، وتثبيته مع تشغيل البت setuid. بالطبع ، ستحتاج إلى اتخاذ احتياطات معقولة لا يمكن الاحتجاج بها خارج برنامجك الرئيسي.

عادة ما يتم ذلك عن طريق جعل الجذر الثنائي الخاص بك.

إحدى طرق إدارة هذا الأمر بحيث تكون الهجمات ضد برنامجك صعبة هي تقليل الكود الذي يتم تشغيله مثل الجذر مثل:

int privileged_server(int argc, char **argv);
int unprivileged_client(int argc, char **argv, int comlink);


int main(int argc, char **argv) {
    int sockets[2];
    pid_t child;
    socketpair(AF_INET, SOCK_STREAM, 0);  /* or is it AF_UNIX? */

    child = fork();
    if (child < 0) {
        perror("fork");
        exit(3);
    } elseif (child == 0) {
        close(sockets[0]);
        dup2(sockets[1], 0);
        close(sockets[1]);
        dup2(0, 1);
        dup2(0, 2); /* or not */
        _exit(privileged_server(argc, argv));
    } else {
        close(sockets[1]);
        int rtn;
        setuid(getuid());
        rtn = unprivileged_client(argc, argv, sockets[0]);
        wait(child);
        return rtn;
    }
}

الآن يتحدث الرمز غير المميز إلى الكود المتميز عبر FD ComLink (وهو مقبس متصل). يستخدم الرمز المتميز المقابل stdin/stdout كنهاية من comLink.

يحتاج الرمز المميز إلى التحقق من أمان كل عملية يحتاج إلى القيام به ، ولكن نظرًا لأن هذا الرمز صغير مقارنة بالرمز غير المميز ، يجب أن يكون هذا سهلاً بشكل معقول.

قد ترغب في إلقاء نظرة على واجهات برمجة التطبيقات هذه:

setuid, seteuid, setgid, setegid, ...

يتم تعريفها في الرأس <unistd.h> في Linux Systems (لا تعرف الكثير عن Mac ، ولكن يجب أن يكون لديك رأس مشابه أيضًا).

إحدى المشكلات التي يمكنني رؤيتها هي أن العملية يجب أن يكون لديها امتيازات كافية لتغيير معرفات المستخدم/المجموعة. وإلا فإن المكالمات إلى الوظائف أعلاه ستؤدي إلى خطأ مع errorno ضبط ل EPERM.

أود أن أقترح عليك تشغيل برنامجك ك root المستخدم ، قم بتغيير معرف المستخدم الفعال (باستخدام seteuid) إلى مستخدم محترم في البداية. ثم ، كلما احتجت إلى رفع الأذونات ، مطالبة بكلمة مرور ثم استخدمها seteuid مرة أخرى للعودة إلى root المستعمل.

على OS X ، يمكنك استخدام AuthorizationExecuteWithPrivileges وظيفة. الصفحة على مهام خدمات التفويض لديه بعض المناقشة التفصيلية حول هذه الوظائف (والذات ذات الصلة).

إليك القليل من رمز C ++ لتنفيذ برنامج بامتيازات المسؤول:

static bool execute(const std::string &program, const std::vector<std::string> &arguments)
{
    AuthorizationRef ref;
    if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) {
        return false;
    }

    AuthorizationItem item = {
        kAuthorizationRightExecute, 0, 0, 0
    };
    AuthorizationRights rights = { 1, &item };
    const AuthorizationFlags flags = kAuthorizationFlagDefaults
                                   | kAuthorizationFlagInteractionAllowed
                                   | kAuthorizationFlagPreAuthorize
                                   | kAuthorizationFlagExtendRights;

    if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) {
        AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
        return false;
    }

    std::vector<char*> args;
    for (std::vector<std::string>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) {
        args.push_back(it->c_str());
    }
    args.push_back(0);

    OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0);

    AuthorizationFree(ref, kAuthorizationFlagDestroyRights);
    return status == errAuthorizationSuccess;
}

يمكنك محاولة تشغيل الأمر لإنشاء الجهاز الظاهري (بما في ذلك SUDO) من خلال قذيفة الخلفية. اطلب كلمة مرور المستخدمين في مربع حوار خاص بك وأنبوبها في القشرة عندما يسأل Sudo ذلك. هناك حلول أخرى مثل استخدام GKSU ، ولكن لا يتم ضمان أن تكون متوفرة على كل جهاز.

لا تقوم بتشغيل برنامجك بالكامل كجذر ، ولكن فقط الجزء الصغير منه يحتاج إلى الجذر. يجب أن تفرخ عملية منفصلة لذلك وقد يكون Sudo المساعدة لك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top