2011-01-31 28 views
8

Tôi có ứng dụng máy chủ sẽ chạy trong tài khoản hệ thống vì tại bất kỳ thời điểm nào, ứng dụng sẽ xử lý yêu cầu thay mặt cho bất kỳ người dùng nào trên hệ thống. Các yêu cầu này bao gồm các hướng dẫn để thao tác hệ thống tệp.Làm thế nào để thực hiện thao tác tập tin/thư mục với các đặc quyền của người dùng?

Đây là nội dung bắt buộc: chương trình cần lưu ý đến đặc quyền của người dùng cụ thể khi thực hiện các tác vụ. Ví dụ: joe không được sửa đổi /home/larry nếu các quyền của nó là 755.

Hiện nay chiến lược của tôi là thế này

  • Lấy chủ sở hữu/nhóm của tập tin
  • So sánh nó với ID người dùng/nhóm ID của người sử dụng cố gắng để thực hiện hành động
  • Nếu một trong hai trận đấu (hoặc nếu không khớp), hãy sử dụng phần thích hợp của trường quyền trong tệp để cho phép hoặc từ chối hành động

Điều này có khôn ngoan không? Có cách nào dễ hơn để thực hiện việc này không? Đầu tiên, tôi đã nghĩ đến việc có nhiều phiên bản ứng dụng đang chạy trong tài khoản của người dùng - nhưng đây không phải là một tùy chọn vì chỉ có một trong các trường hợp có thể nghe trên một cổng TCP nhất định.

Trả lời

3

Hãy xem samba để biết ví dụ về việc này có thể được thực hiện. Các samba daemon chạy như root nhưng dĩa và giả định các thông tin của một người dùng bình thường càng sớm càng tốt.

Hệ thống Unix có hai bộ thông tin xác thực riêng biệt: id người dùng/nhóm thực và id người dùng/nhóm hiệu quả. Bộ thực sự xác định bạn thực sự là ai và tập hợp hiệu quả xác định những gì bạn có thể truy cập. Bạn có thể thay đổi uid/gid hiệu quả như bạn vui lòng nếu bạn là root — bao gồm cả người dùng thông thường và ngược lại — khi id người dùng/nhóm thực của bạn vẫn còn trong quá trình chuyển đổi. Vì vậy, một cách khác để làm điều này trong một quá trình duy nhất là sử dụng seteuid/gid để áp dụng các quyền của người dùng khác nhau qua lại khi cần. Nếu daemon máy chủ của bạn chạy dưới dạng root hoặc có CAP_SETUID thì điều này sẽ được cho phép. Tuy nhiên, lưu ý rằng nếu bạn có khả năng chuyển đổi uid/gid hiệu quả khi ứng dụng của bạn bị lật đổ, thì lật đổ đó có thể chuyển đổi uid/gid thành 0 và bạn có thể có một bảo mật nghiêm trọng dễ bị tổn thương. Đây là lý do tại sao bạn nên loại bỏ tất cả các đặc quyền vĩnh viễn càng sớm càng tốt, bao gồm cả người dùng thực sự của bạn uid/gid.

Vì lý do này bình thường và an toàn hơn để có một ổ cắm nghe duy nhất chạy dưới dạng gốc, sau đó tắt và thay đổi cả id người dùng thực và hiệu quả bằng cách gọi setuid. Sau đó, nó không thể thay đổi trở lại. Quá trình chia đôi của bạn sẽ có ổ cắm là accept() ed vì nó là một ngã ba. Mỗi quá trình chỉ đóng các bộ mô tả tập tin mà chúng không cần; các ổ cắm vẫn còn sống khi chúng được tham chiếu bởi các bộ mô tả tập tin trong các quy trình ngược lại. Bạn cũng có thể thử và thực thi các điều khoản bằng cách kiểm tra riêng từng cá nhân, nhưng tôi hy vọng rõ ràng rằng điều này có khả năng xảy ra lỗi, có nhiều trường hợp cạnh và có nhiều khả năng xảy ra lỗi hơn (ví dụ: làm việc với POSIX ACL trừ khi bạn thực hiện cụ thể quá).

Vì vậy, bạn có ba lựa chọn:

  1. Fork và setgid()/setuid() cho người dùng mà bạn muốn. Nếu giao tiếp là bắt buộc, hãy sử dụng pipe(2) hoặc socketpair(2) trước khi bạn ngã ba.
  2. Không ngã ba và seteuid()/setegid() xung quanh khi cần (ít an toàn: có khả năng dễ dàng xâm phạm máy chủ của bạn).
  3. Không gây rối với thông tin đăng nhập hệ thống; thực thi quyền cho phép theo cách thủ công (kém an toàn hơn: có nhiều khả năng nhận ủy quyền sai).

Nếu bạn cần liên lạc với daemon, thì có thể khó thực hiện nó xuống ổ cắm hoặc ống, tùy chọn đầu tiên thực sự là cách bảo mật phù hợp. Xem ví dụ how ssh does privilege separation. Bạn cũng có thể xem xét nếu có thể thay đổi kiến ​​trúc của bạn vì vậy thay vì bất kỳ thông tin liên lạc, quá trình chỉ có thể chia sẻ một số bộ nhớ hoặc không gian đĩa thay thế.

Bạn đề cập đến việc bạn xem xét việc có một quá trình riêng biệt chạy cho mỗi người dùng, nhưng cần một cổng TCP đang nghe. Bạn vẫn có thể làm điều này. Chỉ cần có một daemon tổng thể lắng nghe trên cổng TCP và gửi các yêu cầu tới mỗi daemon người dùng và giao tiếp theo yêu cầu (ví dụ: thông qua các socket của miền Unix). Điều này thực sự sẽ gần giống như có một daemon chủ forking; Tôi nghĩ rằng sau này sẽ trở nên dễ dàng hơn để thực hiện.

Đọc thêm: trang chủ credentials(7). Cũng lưu ý rằng Linux có hệ thống tập tin uid/gids; điều này gần giống như uid/gids hiệu quả ngoại trừ những thứ khác như gửi tín hiệu. Nếu người dùng của bạn không có quyền truy cập trình bao và không thể chạy mã tùy ý thì bạn không cần phải lo lắng về sự khác biệt.

+0

Cảm ơn bạn đã có câu trả lời rất hay! Nó sẽ có thể tạo ra một quá trình tổng thể lắng nghe trên một cổng và sau đó chuyển tiếp dữ liệu đến một quá trình trên một kết nối socket? Đó có phải là những gì bạn đang nói trong đoạn thứ hai của bạn không? –

+0

Vâng, đúng vậy, và bạn có thể làm điều đó. Tuy nhiên, dựa trên tất cả mọi thứ bạn nói tôi khá chắc chắn rằng nó sẽ được dễ dàng hơn và đơn giản hơn cho bạn để thực hiện một daemon thầy chủ forking và có các đặc quyền thả con.Quá trình chia sẽ tự động có quyền truy cập vào socket yêu cầu giống như daemon chủ; không cần phải truyền đạt phần đó. –

+0

OK. Tôi đang tìm một giải pháp mà sẽ làm việc tốt với Qt là tốt, mặc dù nó không phải là một yêu cầu. Vì vậy, tôi xem xét điều đó khi quyết định cách tiếp cận đúng. –

2

Cho phép một máy chủ chạy trên cổng máy chủ được ưu tiên và tạo ra các quy trình con cho người dùng đăng nhập vào hệ thống. Các tiến trình con sẽ giảm đặc quyền và nhập vai người dùng đã đăng nhập. Giờ thì các con không thể làm hại được nữa.

+0

Có, nhưng các kết nối được chuyển từ máy chủ đến các quy trình con như thế nào? –

+0

Tôi tin rằng điều này hoạt động bằng cách cho phép quá trình gốc thiết lập các ổ cắm, và sau đó chỉ cần vượt qua các mô tả tập tin cho đứa trẻ. Tôi không biết làm thế nào điều này được thực hiện chính xác, nhưng ít nhất PostgreSQL hoạt động theo cách tương tự, ở đâu cho mỗi kết nối, một tiến trình con được sinh ra để xử lý nó. Và tôi không thể tin rằng tất cả các lưu lượng truy cập phải được định tuyến thông qua quá trình chính, vì vậy nó sẽ là một số fd đi qua đó. – Daniel

+0

Vâng ... Tôi sẽ xem xét nó. Điều này làm cho mọi việc trở nên phức tạp hơn, mặc dù bởi vì các tiến trình con phải giao tiếp với cha mẹ. –

3

Tôi sẽ có ngã ba máy chủ() và ngay lập tức setuid(uid) để từ bỏ các đặc quyền gốc. Sau đó, bất kỳ thao tác tệp nào sẽ thay mặt cho người dùng mà bạn đã trở thành. Vì bạn là con của máy chủ, bạn vẫn giữ socket con accept() ed mà yêu cầu (và tôi giả định đáp ứng) sẽ tiếp tục. Điều này (rõ ràng) đòi hỏi đặc quyền root trên daemon.

Việc truyền tệp mô tả giữa các quy trình có vẻ phức tạp không cần thiết trong trường hợp này, vì trẻ đã có bộ mô tả "yêu cầu".

+0

Hmmm ... đó chắc chắn là một khả năng. –

Các vấn đề liên quan