]> git.saurik.com Git - apple/security.git/blob - checkpw/checkpw.c
23c213524fdf61f4175a412e641de29c8fa54f37
[apple/security.git] / checkpw / checkpw.c
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18 /*
19 * checkpw.c
20 * utility to authenticate users using crypt with a fallback on Directory Services
21 *
22 * Copyright: (c) 2000 by Apple Computer, Inc., all rights reserved
23 *
24 */
25
26 #include <pwd.h>
27 #include <stddef.h> // for offsetof()
28 #include <stdlib.h> // for malloc()
29 #include <string.h> // for strcmp()
30 #include <time.h>
31 #include <unistd.h> // for crypt()
32 #include <mach/mach.h>
33 #include <mach/mach_error.h>
34 #include <mach/message.h>
35 #include <servers/bootstrap.h>
36 #include "checkpw.h"
37
38 // begin copied from SharedConsts.h
39 typedef struct {
40 unsigned int msgt_name : 8,
41 msgt_size : 8,
42 msgt_number : 12,
43 msgt_inline : 1,
44 msgt_longform : 1,
45 msgt_deallocate : 1,
46 msgt_unused : 1;
47 } mach_msg_type_t;
48
49 typedef struct sObject
50 {
51 unsigned long type;
52 unsigned long count;
53 unsigned long offset;
54 unsigned long used;
55 unsigned long length;
56 } sObject;
57
58 typedef struct sComData
59 {
60 mach_msg_header_t head;
61 mach_msg_type_t type;
62 unsigned long fDataSize;
63 unsigned long fDataLength;
64 unsigned long fMsgID;
65 unsigned long fPID;
66 unsigned long fPort;
67 unsigned long fIPAddress;
68 sObject obj[ 10 ];
69 char data[ 1 ];
70 } sComData;
71
72 #define kMsgBlockSize (1024 * 4) // Set to average of 4k
73 #define kObjSize (sizeof( sObject ) * 10) // size of object struct
74 #define kIPCMsgLen kMsgBlockSize // IPC message block size
75
76 #define kIPCMsgSize sizeof( sIPCMsg )
77
78 typedef struct sIPCMsg
79 {
80 mach_msg_header_t fHeader;
81 unsigned long fMsgType;
82 unsigned long fCount;
83 unsigned long fOf;
84 unsigned long fMsgID;
85 unsigned long fPID;
86 unsigned long fPort;
87 sObject obj[ 10 ];
88 char fData[ kIPCMsgLen ];
89 mach_msg_security_trailer_t fTail;
90 } sIPCMsg;
91
92 typedef enum {
93 kResult = 4460,
94 ktDataBuff = 4466,
95 } eValueType;
96
97 enum eDSServerCalls {
98 /* 9 */ kCheckUserNameAndPassword = 9
99 };
100 // end copied from SharedConsts.h
101
102 int checkpw_internal( const struct passwd* pw, const char* password );
103
104 int checkpw( const char* userName, const char* password )
105 {
106 struct passwd* pw = NULL;
107 int status;
108
109 // Check username, NULL can crash in getpwnam
110 if (!userName)
111 return CHECKPW_UNKNOWNUSER;
112
113 pw = getpwnam( userName );
114 if (pw == NULL)
115 return CHECKPW_UNKNOWNUSER;
116
117 status = checkpw_internal(pw, password);
118 endpwent();
119 return status;
120 }
121
122 int checkpw_internal( const struct passwd* pw, const char* password )
123 {
124 int siResult = CHECKPW_FAILURE;
125 kern_return_t result = err_none;
126 mach_port_t bsPort = 0;
127 mach_port_t serverPort = 0;
128 mach_port_t replyPort = 0;
129 const char *const srvrName = "DirectoryService";
130 sIPCMsg* msg = NULL;
131 unsigned long len = 0;
132 long curr = 0;
133 unsigned long i = 0;
134
135
136 do {
137 // Special case for empty password (this explicitly denies UNIX-like behavior)
138 if (pw->pw_passwd == NULL || pw->pw_passwd[0] == '\0') {
139 if (password == NULL || password[0] == '\0')
140 siResult = CHECKPW_SUCCESS;
141 else
142 siResult = CHECKPW_BADPASSWORD;
143
144 break;
145 }
146
147 // check password, NULL crashes crypt()
148 if (!password)
149 {
150 siResult = CHECKPW_BADPASSWORD;
151 break;
152 }
153 // Correct password hash
154 if (strcmp(crypt(password, pw->pw_passwd), pw->pw_passwd) == 0) {
155 siResult = CHECKPW_SUCCESS;
156 break;
157 }
158
159 // Try Directory Services directly
160
161 result = mach_port_allocate( mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort );
162 if ( result != err_none ) {
163 siResult = CHECKPW_FAILURE;
164 break;
165 }
166
167 result = task_get_bootstrap_port( mach_task_self(), &bsPort );
168 if ( result != err_none ) {
169 siResult = CHECKPW_FAILURE;
170 break;
171 }
172
173 // check if DirectoryService is alive
174 result = bootstrap_look_up( bsPort, (char *)srvrName, &serverPort );
175 if ( result != err_none ) {
176 siResult = CHECKPW_FAILURE;
177 break;
178 }
179
180 // ask directory services to do auth
181 msg = calloc( sizeof( sIPCMsg ), 1 );
182 if ( msg == NULL ) {
183 siResult = CHECKPW_FAILURE; // memory error
184 break;
185 }
186
187 // put username and password into message
188 msg->obj[0].type = ktDataBuff;
189 msg->obj[0].count = 1;
190 msg->obj[0].offset = offsetof(struct sComData, data);
191
192 // User Name
193 len = strlen( pw->pw_name );
194 if (curr + len + sizeof(unsigned long) > kIPCMsgLen)
195 {
196 siResult = CHECKPW_FAILURE;
197 break;
198 }
199 memcpy( &(msg->fData[ curr ]), &len, sizeof( unsigned long ) );
200 curr += sizeof( unsigned long );
201 memcpy( &(msg->fData[ curr ]), pw->pw_name, len );
202 curr += len;
203
204 // Password
205 len = strlen( password );
206 if (curr + len + sizeof(unsigned long) > kIPCMsgLen)
207 {
208 siResult = CHECKPW_FAILURE;
209 break;
210 }
211 memcpy( &(msg->fData[ curr ]), &len, sizeof( unsigned long ) );
212 curr += sizeof ( unsigned long );
213 memcpy( &(msg->fData[ curr ]), password, len );
214 curr += len;
215 msg->obj[0].used = curr;
216 msg->obj[0].length = curr;
217
218 msg->fHeader.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND );
219 msg->fHeader.msgh_size = sizeof( sIPCMsg ) - sizeof( mach_msg_security_trailer_t );
220 msg->fHeader.msgh_id = kCheckUserNameAndPassword;
221 msg->fHeader.msgh_remote_port = serverPort;
222 msg->fHeader.msgh_local_port = replyPort;
223
224 msg->fMsgType = kCheckUserNameAndPassword;
225 msg->fCount = 1;
226 msg->fOf = 1;
227 msg->fPort = replyPort;
228 msg->fPID = getpid();
229 msg->fMsgID = time( NULL ) + kCheckUserNameAndPassword;
230
231 // send the message
232 result = mach_msg_send( (mach_msg_header_t*)msg );
233 if ( result != MACH_MSG_SUCCESS ) {
234 siResult = CHECKPW_FAILURE;
235 break;
236 }
237
238 // get reply
239 memset( msg, 0, kIPCMsgLen );
240
241 result = mach_msg( (mach_msg_header_t *)msg, MACH_RCV_MSG | MACH_RCV_TIMEOUT,
242 0, kIPCMsgSize, replyPort, 300 * 1000, MACH_PORT_NULL );
243
244 if ( result != MACH_MSG_SUCCESS ) {
245 siResult = CHECKPW_FAILURE;
246 break;
247 }
248
249 if ( msg->fCount != 1 ) {
250 // couldn't get reply
251 siResult = CHECKPW_FAILURE;
252 break;
253 }
254
255 for (i = 0; i < 10; i++ )
256 {
257 if ( msg->obj[ i ].type == (unsigned long)kResult )
258 {
259 siResult = msg->obj[ i ].count;
260 break;
261 }
262 }
263
264 } while (0);
265
266 if (msg != NULL) {
267 free(msg);
268 msg = NULL;
269 }
270
271 // deallocate the serverPort
272 mach_port_deallocate( mach_task_self(), serverPort);
273
274 if ( replyPort != 0 )
275 mach_port_destroy( mach_task_self(), replyPort );
276
277
278 return siResult;
279 }