2016-11-16 17 views
5

Chúng tôi có một Daemon khởi chạy (nhất thiết, vì nhiều lý do) chạy dưới dạng root và giao tiếp với một thành phần máy chủ qua mạng. Nó cần phải xác thực với dịch vụ, vì vậy khi nó lần đầu tiên có được mật khẩu, chúng tôi lưu nó vào keychain hệ thống. Trong lần khởi chạy tiếp theo, ý tưởng là lấy lại mật khẩu từ móc khóa và sử dụng nó để xác thực với dịch vụ mạng.Mac Khởi động Daemon không thể lấy mật khẩu từ móc khóa hệ thống sau khi lưu nó ở đó

Điều này đã hoạt động tốt, nhưng trên macOS 10.12 mã hiện tại ngừng hoạt động và chúng tôi đã hoàn toàn bối rối về cách sửa lỗi này. Nó nắm này:

Cho dù chúng ta đang tiết kiệm một mật khẩu mới hoặc lấy một cái cũ, chúng tôi có được một tham chiếu đến keychain hệ thống sử dụng này:

SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem, &system_keychain); 

Chúng tôi cũng vô hiệu hóa tương tác người dùng cho biện pháp tốt, mặc dù chúng tôi hy vọng nó đã được tắt trong bối cảnh của một daemon.

SecKeychainSetUserInteractionAllowed(false); 

Khi lưu một mật khẩu mới cho keychain, chúng tôi sử dụng

OSStatus status = SecKeychainAddInternetPassword(
    system_keychain, 
    urlLength, server_base_url, 
    0, NULL, 
    usernameLength, username, 
    0, NULL, 
    0, 
    kSecProtocolTypeAny, kSecAuthenticationTypeAny, 
    passwordLength, password, 
    NULL); 

này nhiều công trình. Thành công được báo cáo và tôi có thể thấy mục trong keychain "hệ thống" trong Keychain Access.app.

Lấy nó trên chạy tiếp theo của daemon của chúng tôi được thực hiện với dòng này:

status = SecKeychainFindInternetPassword(
    system_keychain, 
    urlLength, url, 
    0, NULL, 
    usernameLength, username, 
    0, NULL, 
    0, 
    kSecProtocolTypeAny, kSecAuthenticationTypeAny, 
    &passwordLength, &password_data, 
    NULL); 

Thật không may, điều này đã bắt đầu quay trở lại errSecAuthFailed vì lý do đó không rõ ràng đối với chúng tôi.

Một vài chi tiết thêm, chúng tôi đã kiểm tra và điều chúng tôi đã cố gắng, không có kết quả:

  • Các nhị phân daemon được ký kết với một chứng chỉ Id phát triển.
  • Mã nhị phân daemon chứa phần Info.plist được nhúng với ID gói và phiên bản.
  • Tôi có thể xem nhị phân daemon trong danh sách "Luôn cho phép truy cập bởi các ứng dụng này" trong tab "Kiểm soát truy cập" của mục mật khẩu trong Keychain Access.app.
  • Nếu tôi tự chuyển sang "Cho phép tất cả ứng dụng truy cập mục này" trong Keychain Access, nó hoạt động. Điều này phần nào đánh bại các điểm tiết kiệm mật khẩu trong keychain, tuy nhiên.
  • Chúng tôi đã thử chơi xung quanh với các tham số cho SecKeychainAddInternetPassword, nhưng điều này dường như không tạo ra bất kỳ sự khác biệt nào.
  • Chúng tôi đã cố gắng mở khoá một cách rõ ràng với SecKeychainUnlock(), nhưng như tài liệu cho thấy, điều này có vẻ không cần thiết.
  • Xóa mục trong Keychain Access.app gây ra SecKeychainFindInternetPassword() để mang lại errSecItemNotFound, như bạn mong đợi. Vì vậy, nó có thể chắc chắn tìm thấy mục đã lưu, nó không được phép đọc nó.

Tài liệu móc khóa không phải là dễ đọc chính xác và ở các phần thay đổi theo kinh nghiệm. ("Để làm Y, bạn cần phải làm Y", mà không đề cập đến lý do tại sao bạn muốn làm Y.) Tuy nhiên, tôi nghĩ rằng tôi đã thực hiện nó thông qua và đã hiểu hầu hết của nó.Các khía cạnh khác nhau của thiết lập cụ thể của chúng tôi không được đề cập chi tiết (truy cập từ một daemon), nhưng có vẻ khá rõ ràng khi truy cập một mục trước đây được lưu bởi cùng một ứng dụng không yêu cầu bất kỳ ủy quyền hoặc xác thực đặc biệt nào. Đó là mâu thuẫn trực tiếp với hành vi mà chúng ta đang thấy.

Bất kỳ ý tưởng nào?

Trả lời

7

Sau khi dành thêm vài giờ để thực hiện việc này trong vài ngày, chúng tôi đã tìm ra những gì đang diễn ra.

Trước tiên, tôi đã cố tạo một ví dụ tối thiểu có thể tái tạo sự cố. Điều này không thành công với errSecAuthFailed và do đó không tái tạo sự cố. Vì vậy, trở lại daemon ban đầu, phải có một cái gì đó đặc biệt về nó mà đã đi sai.

Ý tưởng tiếp theo là kiểm tra nhật ký hệ thống trong thời gian SecKeychainFindInternetPassword() được gọi. Điều này đã bật lên một số thông báo lỗi:

securityd CSSM Exception: -2147411889 CSSMERR_CL_UNKNOWN_TAG 
securityd MacOS error: -67063 
securityd MacOS error: -67063 
securityd code requirement check failed (-67063), client is not Apple-signed 
securityd CSSM Exception: 32 CSSM_ERRCODE_OPERATION_AUTH_DENIED 
OurDaemon subsystem: com.apple.securityd, category: security_exception, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 2, enable_private_data: 0 
OurDaemon CSSM Exception: -2147416032 CSSMERR_CSP_OPERATION_AUTH_DENIED 

Vấn đề này có thể là do ký mã. Lạ thật. Kiểm tra chữ ký mã của mã nhị phân với codesign -vv không trả lại sự cố nào.

Sau khi khám phá trên web cho các phần khác nhau của thông báo lỗi, tôi đã tìm thấy -67063 corresponds to errSecCSGuestInvalid. Nhận xét lần đọc "mã nhận dạng đã bị vô hiệu."

Được rồi, chắc chắn một số lỗi mã hóa, nhưng điều đó có nghĩa là gì và tại sao nó lại xảy ra?

Săn bắn xung quanh một số cuối cùng hơn bật lên những lời giải thích, và cũng là giải pháp: http://lists.apple.com/archives/apple-cdsa/2010/Mar/msg00027.html

Nó có nghĩa là tại một số điểm kể từ khi chương trình đã bắt đầu, một cái gì đó đã xảy ra với nó mà làm cho nó không hợp lệ.

nếu bạn chạy một chương trình ký kết, sau đó thay thế nó (bằng cách, ví dụ, xây dựng một phiên bản mới tại chỗ :-), và sau đó chạy phiên bản mới, hạt nhân sẽ vẫn giữ chữ ký cũ được đính kèm với vnode của tệp thi hành. Nếu đó là tình huống của bạn, chỉ cần xóa tệp thi hành và tạo lại nó sẽ giải quyết vấn đề cho tốt (cho đến khi bạn ghi đè lại tệp :-). Chúng tôi khuyên bạn nên luôn thay thế mã đã ký (mv (1), không phải cp (1) hoặc tương đương).

Điều này giải thích. Tôi đã sao chép các phiên bản mới của daemon vào vị trí sử dụng

sudo cp path/to/built/daemon /usr/local/libexec/ 

Rõ ràng, đó là ghi đè file tại chỗ thay vì tạo ra một vnode mới, viết rằng, và sau đó đổi tên nó trong tập tin cũ. Vì vậy, giải pháp là để cp vào thư mục tạm thời trước và sau đó mv vào vị trí. Hoặc xóa tệp đích trước khi sử dụng cp.

Ngay khi tôi làm điều đó, nó đã hoạt động!

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